1 /* This file is part of the GNU plotutils package.  Copyright (C) 1995,
2    1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3 
4    The GNU plotutils package is free software.  You may redistribute it
5    and/or modify it under the terms of the GNU General Public License as
6    published by the Free Software foundation; either version 2, or (at your
7    option) any later version.
8 
9    The GNU plotutils package is distributed in the hope that it will be
10    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with the GNU plotutils package; see the file COPYING.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17    Boston, MA 02110-1301, USA. */
18 
19 /* This file defines the initialization for any CGMPlotter object,
20    including both private data and public methods.  There is a one-to-one
21    correspondence between public methods and user-callable functions in the
22    C API. */
23 
24 #include "sys-defines.h"
25 #include "extern.h"
26 
27 /* localtime_r() is currently not used, because there is apparently _no_
28    universal way of ensuring that it is declared.  On some systems
29    (e.g. Red Hat Linux), `#define _POSIX_SOURCE' will do it.  But on other
30    systems, doing `#define _POSIX_SOURCE' **removes** the declaration! */
31 #ifdef HAVE_LOCALTIME_R
32 #undef HAVE_LOCALTIME_R
33 #endif
34 
35 #ifdef MSDOS
36 #include <unistd.h>		/* for fsync() */
37 #endif
38 
39 /* song and dance to define time_t, and declare both time() and localtime() */
40 #ifdef HAVE_SYS_TYPES_H
41 #include <sys/types.h>		/* for time_t on some pre-ANSI Unix systems */
42 #endif
43 #ifdef TIME_WITH_SYS_TIME
44 #include <sys/time.h>		/* for time() on some pre-ANSI Unix systems */
45 #include <time.h>		/* for localtime() */
46 #else  /* not TIME_WITH_SYS_TIME, include only one (prefer <sys/time.h>) */
47 #ifdef HAVE_SYS_TIME_H
48 #include <sys/time.h>
49 #else  /* not HAVE_SYS_TIME_H */
50 #include <time.h>
51 #endif /* not HAVE_SYS_TIME_H */
52 #endif /* not TIME_WITH_SYS_TIME */
53 
54 /* forward references */
55 static void build_sdr_from_index (plOutbuf *sdr_buffer, int cgm_encoding, int x);
56 static void build_sdr_from_string (plOutbuf *sdr_buffer, int cgm_encoding, const char *s, int string_length, bool use_double_quotes);
57 static void build_sdr_from_ui8s (plOutbuf *sdr_buffer, int cgm_encoding, const int *x, int n);
58 
59 #ifndef LIBPLOTTER
60 /* In libplot, this is the initialization for the function-pointer part of
61    a CGMPlotter struct. */
62 const Plotter _pl_c_default_plotter =
63 {
64   /* initialization (after creation) and termination (before deletion) */
65   _pl_c_initialize, _pl_c_terminate,
66   /* page manipulation */
67   _pl_c_begin_page, _pl_c_erase_page, _pl_c_end_page,
68   /* drawing state manipulation */
69   _pl_g_push_state, _pl_g_pop_state,
70   /* internal path-painting methods (endpath() is a wrapper for the first) */
71   _pl_c_paint_path, _pl_c_paint_paths, _pl_g_path_is_flushable, _pl_g_maybe_prepaint_segments,
72   /* internal methods for drawing of markers and points */
73   _pl_c_paint_marker, _pl_c_paint_point,
74   /* internal methods that plot strings in Hershey, non-Hershey fonts */
75   _pl_g_paint_text_string_with_escapes, _pl_c_paint_text_string,
76   _pl_g_get_text_width,
77   /* private low-level `retrieve font' method */
78   _pl_g_retrieve_font,
79   /* `flush output' method, called only if Plotter handles its own output */
80   _pl_g_flush_output,
81   /* error handlers */
82   _pl_g_warning,
83   _pl_g_error,
84 };
85 #endif /* not LIBPLOTTER */
86 
87 /* The private `initialize' method, which is invoked when a Plotter is
88    created.  It is used for such things as initializing capability flags
89    from the values of class variables, allocating storage, etc.  When this
90    is invoked, _plotter points to the Plotter that has just been
91    created. */
92 
93 void
_pl_c_initialize(S___ (Plotter * _plotter))94 _pl_c_initialize (S___(Plotter *_plotter))
95 {
96 #ifndef LIBPLOTTER
97   /* in libplot, manually invoke superclass initialization method */
98   _pl_g_initialize (S___(_plotter));
99 #endif
100 
101   /* override generic initializations (which are appropriate to the base
102      Plotter class), as necessary */
103 
104 #ifndef LIBPLOTTER
105   /* tag field, differs in derived classes */
106   _plotter->data->type = PL_CGM;
107 #endif
108 
109   /* output model */
110   _plotter->data->output_model = PL_OUTPUT_PAGES_ALL_AT_ONCE;
111 
112   /* user-queryable capabilities: 0/1/2 = no/yes/maybe */
113   _plotter->data->have_wide_lines = 1;
114   _plotter->data->have_dash_array = 0;
115   _plotter->data->have_solid_fill = 1;
116   _plotter->data->have_odd_winding_fill = 1;
117   _plotter->data->have_nonzero_winding_fill = 0;
118   _plotter->data->have_settable_bg = 1;
119   _plotter->data->have_escaped_string_support = 0;
120   _plotter->data->have_ps_fonts = 1;
121   _plotter->data->have_pcl_fonts = 0;
122   _plotter->data->have_stick_fonts = 0;
123   _plotter->data->have_extra_stick_fonts = 0;
124   _plotter->data->have_other_fonts = 0;
125 
126   /* text and font-related parameters (internal, not queryable by user);
127      note that we don't set kern_stick_fonts, because it was set by the
128      superclass initialization (and it's irrelevant for this Plotter type,
129      anyway) */
130   _plotter->data->default_font_type = PL_F_POSTSCRIPT;
131   _plotter->data->pcl_before_ps = false;
132   _plotter->data->have_horizontal_justification = true;
133   _plotter->data->have_vertical_justification = true;
134   _plotter->data->issue_font_warning = true;
135 
136   /* path-related parameters (also internal); note that we
137      don't set max_unfilled_path_length, because it was set by the
138      superclass initialization */
139   _plotter->data->have_mixed_paths = false;
140   _plotter->data->allowed_arc_scaling = AS_NONE;
141   _plotter->data->allowed_ellarc_scaling = AS_NONE;
142   _plotter->data->allowed_quad_scaling = AS_NONE;
143   _plotter->data->allowed_cubic_scaling = AS_NONE;
144   _plotter->data->allowed_box_scaling = AS_AXES_PRESERVED;
145   _plotter->data->allowed_circle_scaling = AS_UNIFORM;
146   _plotter->data->allowed_ellipse_scaling = AS_ANY;
147 
148   /* dimensions */
149   _plotter->data->display_model_type = (int)DISP_MODEL_VIRTUAL;
150   _plotter->data->display_coors_type = (int)DISP_DEVICE_COORS_INTEGER_NON_LIBXMI;
151   _plotter->data->flipped_y = false;
152       /* we choose viewport coor range to be 1/4 of the integer range */
153   _plotter->data->imin = - ((1 << (8*CGM_BINARY_BYTES_PER_INTEGER - 3)) - 1);
154   _plotter->data->imax = (1 << (8*CGM_BINARY_BYTES_PER_INTEGER - 3)) - 1;
155   _plotter->data->jmin = - ((1 << (8*CGM_BINARY_BYTES_PER_INTEGER - 3)) - 1);
156   _plotter->data->jmax = (1 << (8*CGM_BINARY_BYTES_PER_INTEGER - 3)) - 1;
157   _plotter->data->xmin = 0.0;
158   _plotter->data->xmax = 0.0;
159   _plotter->data->ymin = 0.0;
160   _plotter->data->ymax = 0.0;
161   _plotter->data->page_data = (plPageData *)NULL;
162 
163   /* initialize data members specific to this derived class */
164   /* parameters */
165   _plotter->cgm_encoding = CGM_ENCODING_BINARY;
166   _plotter->cgm_max_version = 4;
167   /* most important dynamic variables (global) */
168   _plotter->cgm_version = 1;
169   _plotter->cgm_profile = CGM_PROFILE_WEB;
170   _plotter->cgm_need_color = false;
171   /* corresponding dynamic variables (page-specific, i.e. picture-specific) */
172   _plotter->cgm_page_version = 1;
173   _plotter->cgm_page_profile = CGM_PROFILE_WEB;
174   _plotter->cgm_page_need_color = false;
175   /* colors (24-bit or 48-bit, initialized to nonphysical or dummy values) */
176   _plotter->cgm_line_color.red = -1;
177   _plotter->cgm_line_color.green = -1;
178   _plotter->cgm_line_color.blue = -1;
179   _plotter->cgm_edge_color.red = -1;
180   _plotter->cgm_edge_color.green = -1;
181   _plotter->cgm_edge_color.blue = -1;
182   _plotter->cgm_fillcolor.red = -1;
183   _plotter->cgm_fillcolor.green = -1;
184   _plotter->cgm_fillcolor.blue = -1;
185   _plotter->cgm_marker_color.red = -1;
186   _plotter->cgm_marker_color.green = -1;
187   _plotter->cgm_marker_color.blue = -1;
188   _plotter->cgm_text_color.red = -1;
189   _plotter->cgm_text_color.green = -1;
190   _plotter->cgm_text_color.blue = -1;
191   _plotter->cgm_bgcolor.red = -1; /* set in c_begin_page() */
192   _plotter->cgm_bgcolor.green = -1;
193   _plotter->cgm_bgcolor.blue = -1;
194   /* other dynamic variables */
195   _plotter->cgm_line_type = CGM_L_SOLID;
196   _plotter->cgm_dash_offset = 0.0;
197   _plotter->cgm_join_style = CGM_JOIN_UNSPEC;
198   _plotter->cgm_cap_style = CGM_CAP_UNSPEC;
199   _plotter->cgm_dash_cap_style = CGM_CAP_UNSPEC;
200   	/* CGM's default line width: 1/1000 times the max VDC dimension */
201   _plotter->cgm_line_width = (1 << (8*CGM_BINARY_BYTES_PER_INTEGER - 3)) / 500;
202   _plotter->cgm_interior_style = CGM_INT_STYLE_HOLLOW;
203   _plotter->cgm_edge_type = CGM_L_SOLID;
204   _plotter->cgm_edge_dash_offset = 0.0;
205   _plotter->cgm_edge_join_style = CGM_JOIN_UNSPEC;
206   _plotter->cgm_edge_cap_style = CGM_CAP_UNSPEC;
207   _plotter->cgm_edge_dash_cap_style = CGM_CAP_UNSPEC;
208   	/* CGM's default edge width: 1/1000 times the max VDC dimension */
209   _plotter->cgm_edge_width = (1 << (8*CGM_BINARY_BYTES_PER_INTEGER - 3)) / 500;
210   _plotter->cgm_edge_is_visible = false;
211   _plotter->cgm_miter_limit = 32767.0;
212   _plotter->cgm_marker_type = CGM_M_ASTERISK;
213   	/* CGM's default marker size: 1/1000 times the max VDC dimension */
214   _plotter->cgm_marker_size = (1 << (8*CGM_BINARY_BYTES_PER_INTEGER - 3)) /500;
215   	/* label-related variables */
216   _plotter->cgm_char_height = -1; /* impossible (dummy) value */
217   _plotter->cgm_char_base_vector_x = 1;
218   _plotter->cgm_char_base_vector_y = 0;
219   _plotter->cgm_char_up_vector_x = 0;
220   _plotter->cgm_char_up_vector_y = 1;
221   _plotter->cgm_horizontal_text_alignment = CGM_ALIGN_NORMAL_HORIZONTAL;
222   _plotter->cgm_vertical_text_alignment = CGM_ALIGN_NORMAL_VERTICAL;
223   _plotter->cgm_font_id = -1;	/* impossible (dummy) value */
224   _plotter->cgm_charset_lower = 0; /* dummy value (we use values 1..4) */
225   _plotter->cgm_charset_upper = 0; /* dummy value (we use values 1..4) */
226   _plotter->cgm_restricted_text_type = CGM_RESTRICTED_TEXT_TYPE_BASIC;
227 
228   /* initialize certain data members from device driver parameters */
229 
230   /* determine page type, and viewport size and location */
231   _set_page_type (_plotter->data);
232 
233   /* user may have specified a viewport aspect ratio other than 1:1, so
234      carefully compute device-space coordinate ranges (i.e. don't use above
235      default values for imin,imax,jmin,jmax, which are appropriate only for
236      a square viewport) */
237   {
238     /* our choice: the larger side of the viewport will essentially be 1/4
239        times the maximum range for integer device coordinates, i.e., half
240        the larger side will be 1/8 times the maximum range */
241     int half_side = (1 << (8*CGM_BINARY_BYTES_PER_INTEGER - 3)) - 1;
242     int half_other_side;
243     double xsize = _plotter->data->viewport_xsize;
244     double ysize = _plotter->data->viewport_ysize;
245     int xsign = xsize < 0.0 ? -1 : 1;
246     int ysign = ysize < 0.0 ? -1 : 1;
247     double fraction;
248 
249     /* There are two cases, plus a degenerate case.  For each,
250        `scaling_factor' is the conversion factor from virtual to physical
251        units. */
252 
253     if (xsize == 0.0 && ysize == 0.0)
254       /* degenerate case, scaling_factor = 0 (or anything else :-)) */
255       {
256 	_plotter->data->imin = 0;
257 	_plotter->data->imax = 0;
258 	_plotter->data->jmin = 0;
259 	_plotter->data->jmax = 0;
260       }
261     else if (FABS(ysize) > FABS(xsize))
262       /* scaling_factor = FABS(ysize) / (2*half_side) */
263       {
264 	fraction = FABS(xsize) / FABS(ysize);
265 	half_other_side = IROUND(half_side * fraction);
266 	_plotter->data->imin = - xsign * half_other_side;
267 	_plotter->data->imax = xsign * half_other_side;
268 	_plotter->data->jmin = - ysign * half_side;
269 	_plotter->data->jmax = ysign * half_side;
270       }
271     else		/* FABS(ysize) <= FABS(xsize), which is nonzero */
272       /* scaling_factor = FABS(xsize) / (2*half_side) */
273       {
274 	fraction = FABS(ysize) / FABS(xsize);
275 	half_other_side = IROUND(half_side * fraction);
276 	_plotter->data->imin = - xsign * half_side;
277 	_plotter->data->imax = xsign * half_side;
278 	_plotter->data->jmin = - ysign * half_other_side;
279 	_plotter->data->jmax = ysign * half_other_side;
280       }
281   }
282 
283   /* compute the NDC to device-frame affine map, set it in Plotter */
284   _compute_ndc_to_device_map (_plotter->data);
285 
286   /* determine CGM encoding */
287   {
288     const char* cgm_encoding_type;
289 
290     cgm_encoding_type =
291       (const char *)_get_plot_param (_plotter->data, "CGM_ENCODING");
292     if (cgm_encoding_type != NULL)
293       {
294 	if (strcmp (cgm_encoding_type, "binary") == 0)
295 	  _plotter->cgm_encoding = CGM_ENCODING_BINARY;
296 	else if (strcmp (cgm_encoding_type, "clear text") == 0
297 		 || (strcmp (cgm_encoding_type, "cleartext") == 0)
298 		 || (strcmp (cgm_encoding_type, "clear_text") == 0))
299 	  _plotter->cgm_encoding = CGM_ENCODING_CLEAR_TEXT;
300 	else			/* we don't support the character encoding */
301 	  _plotter->cgm_encoding = CGM_ENCODING_BINARY;
302       }
303     else
304       _plotter->cgm_encoding = CGM_ENCODING_BINARY; /* default value */
305   }
306 
307   /* determine upper bound on CGM version number */
308   {
309     const char* cgm_max_version_type;
310 
311     cgm_max_version_type =
312       (const char *)_get_plot_param (_plotter->data, "CGM_MAX_VERSION");
313     if (cgm_max_version_type != NULL)
314       {
315 	if (strcmp (cgm_max_version_type, "1") == 0)
316 	  _plotter->cgm_max_version = 1;
317 	else if (strcmp (cgm_max_version_type, "2") == 0)
318 	  _plotter->cgm_max_version = 2;
319 	else if (strcmp (cgm_max_version_type, "3") == 0)
320 	  _plotter->cgm_max_version = 3;
321 	else if (strcmp (cgm_max_version_type, "4") == 0)
322 	  _plotter->cgm_max_version = 4;
323 	else			/* use default */
324 	  _plotter->cgm_max_version = 4;
325       }
326     else
327       _plotter->cgm_max_version = 4; /* use default */
328   }
329 
330   /* If the maximum CGM version number is greater than 1, relax the
331      constraints on what path segments can be stored in libplot's path
332      buffer.  By default, we allow only line segments. */
333 
334   /* Counterclockwise circular arcs have been in the CGM standard since
335      version 1, but clockwise circular arcs were only added in version 2.
336      To include a circular arc we insist on a uniform map from user to
337      device coordinates, since otherwise it wouldn't be mapped to a
338      circle.
339 
340      Similarly, we don't allow elliptic arcs into the arc buffer unless the
341      version is 2 or higher.  Elliptic arcs have been in the standard since
342      version 1, but the `closed figure' construction that we use to fill
343      single elliptic (and circular!) arcs was only added in version 2. */
344   if (_plotter->cgm_max_version >= 2)
345     {
346       _plotter->data->allowed_arc_scaling = AS_UNIFORM;
347       _plotter->data->allowed_ellarc_scaling = AS_ANY;
348     }
349 
350   /* Bezier cubics were added to the standard in version 3.  Closed mixed
351      paths (`closed figures' in CGM jargon) have been in the standard since
352      version 2, but open mixed paths (`compound lines' in CGM jargon) were
353      only added in version 3. */
354   if (_plotter->cgm_max_version >= 3)
355     {
356       _plotter->data->allowed_cubic_scaling = AS_ANY;
357       _plotter->data->have_mixed_paths = true;
358     }
359 
360   /* Beginning in version 3 CGM's, user can define line types, by
361      specifying a precise dashing style. */
362   if (_plotter->cgm_max_version >= 3)
363     _plotter->data->have_dash_array = 1;
364 }
365 
366 /* Lists of metafile elements that we use, indexed by the CGM version less
367    unity, i.e., by 0,1,2,3 for CGM versions 1,2,3,4.  We use a standard
368    shorthand: element class -1, and element id 0, 1, 2, etc., specifies
369    certain sets of metafile elements.  E.g., class=-1 and id=1 specifies
370    all the version-1 metafile elements; in the clear text encoding this is
371    written as "DRAWINGPLUS". */
372 
373 #define MAX_CGM_ELEMENT_LIST_LENGTH 1
374 typedef struct
375 {
376   const char *text_string;
377   int length;			/* number of genuine entries in list */
378   int class_id[MAX_CGM_ELEMENT_LIST_LENGTH];
379   int element_id[MAX_CGM_ELEMENT_LIST_LENGTH];
380 }
381 plCGMElementList;
382 
383 static const plCGMElementList _metafile_element_list[4] =
384 {
385   /* version 1 */
386   { "DRAWINGPLUS", 1, {-1}, {1} },
387   /* version 2 */
388   { "VERSION2",    1, {-1}, {2} },
389   /* version 3 */
390   { "VERSION3",    1, {-1}, {5} },
391   /* version 4 */
392   { "VERSION4",    1, {-1}, {6} }
393 };
394 
395 /* CGM character sets, for upper and lower halves of both ISO-Latin-1 and
396    Symbol fonts.  We use standard 8-bit encoding when writing text strings
397    in either sort of font, but the character set used in each font half
398    needs to be specified explicitly.  Supported types of character set
399    include "standard 94-character set" and "standard 96-character set".
400    The character set is further specified by the "tail" string. */
401 
402 typedef struct
403 {
404   int type;			/* a CGM enumerative */
405   const char *type_string;	/* its string representation, for cleartext */
406   const char *tail;		/* the `designation sequence tail' */
407 }
408 plCGMCharset;
409 
410 static const plCGMCharset _iso_latin_1_cgm_charset[2] =
411 {
412   { 0, "std94", "4/2" },	/* ISO 8859-1 LH, tail is "A" */
413   { 1, "std96", "4/1" }		/* ISO 8859-1 RH, tail is "B" */
414 };
415 
416 static const plCGMCharset _symbol_cgm_charset[2] =
417 {
418   { 0, "std94", "2/10 3/10" },	/* Symbol LH, tail is "*:" */
419   { 0, "std94", "2/6 3/10" }	/* Symbol RH, tail is "&:" */
420 };
421 
422 /* The private `terminate' method, which is invoked when a Plotter is
423    deleted.  It may do such things as write to an output stream from
424    internal storage, deallocate storage, etc.  When this is invoked,
425    _plotter points to the Plotter that is about to be deleted. */
426 
427 /* This version is for CGM Plotters...
428 
429    (CGM Plotters differ from most other plotters that do not plot in real
430    time in that they emit output only after all pages have pages have been
431    drawn, rather than at the end of each page.  This is necessary in order
432    to produce the correct header lines.)
433 
434    When this is called, the CGM code for the body of each page is stored in
435    a plOutbuf, and the page plOutbufs form a linked list. */
436 
437 void
_pl_c_terminate(S___ (Plotter * _plotter))438 _pl_c_terminate (S___(Plotter *_plotter))
439 {
440   int i;
441   plOutbuf *current_page;
442   bool ps_font_used_in_doc[PL_NUM_PS_FONTS];
443   bool symbol_font_used_in_doc;
444   bool cgm_font_id_used_in_doc[PL_NUM_PS_FONTS];
445   bool doc_uses_fonts;
446   int max_cgm_font_id;
447 
448   /* if no pages of graphics (i.e. Plotter was never opened), CGM file
449      won't contain any pictures, and won't satisfy any standard profile */
450   if (_plotter->data->first_page == (plOutbuf *)NULL)
451     _plotter->cgm_profile =
452       IMAX(_plotter->cgm_profile, CGM_PROFILE_NONE);
453 
454   /* COMMENTED OUT BECAUSE USERS WOULD FIND THIS TOO CONFUSING! */
455 #if 0
456   /* only the binary encoding satisfies the WebCGM profile */
457   if (_plotter->cgm_encoding != CGM_ENCODING_BINARY)
458     _plotter->cgm_profile =
459       IMAX(_plotter->cgm_profile, CGM_PROFILE_MODEL);
460 #endif
461 
462 #ifdef LIBPLOTTER
463   if (_plotter->data->outfp || _plotter->data->outstream)
464 #else
465   if (_plotter->data->outfp)
466 #endif
467     /* have an output stream, will emit CGM commands */
468     {
469       plOutbuf *doc_header, *doc_trailer;
470       int byte_count, data_byte_count, data_len, string_length;
471 
472       doc_header = _new_outbuf ();
473 
474       /* emit "BEGIN METAFILE" command */
475       {
476 	const char *string_param;
477 
478 	string_param = "CGM plot";
479 	string_length = strlen (string_param);
480 	data_len = CGM_BINARY_BYTES_PER_STRING(string_length);
481 	byte_count = data_byte_count = 0;
482 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
483 				  CGM_DELIMITER_ELEMENT, 1,
484 				  data_len, &byte_count,
485 				  "BEGMF");
486 	_cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
487 			  string_param,
488 			  string_length, true,
489 			  data_len, &data_byte_count, &byte_count);
490 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
491 				      &byte_count);
492       }
493 
494       /* emit "METAFILE VERSION" command */
495       {
496 	data_len = CGM_BINARY_BYTES_PER_INTEGER;
497 	byte_count = data_byte_count = 0;
498 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
499 				  CGM_METAFILE_DESCRIPTOR_ELEMENT, 1,
500 				  data_len, &byte_count,
501 				  "MFVERSION");
502 	_cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
503 			   _plotter->cgm_version,
504 			   data_len, &data_byte_count, &byte_count);
505 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
506 				      &byte_count);
507       }
508 
509       /* emit "METAFILE ELEMENT LIST" command; this is encoding-dependent */
510 
511       {
512 	const plCGMElementList *element_list =
513 	  &(_metafile_element_list[_plotter->cgm_version - 1]);
514 	int length = element_list->length;
515 	int k;
516 
517 	/* 1 integer, plus `length' pairs of 2-byte indices */
518 	data_len =  CGM_BINARY_BYTES_PER_INTEGER + 2 * 2 * length;
519 	byte_count = data_byte_count = 0;
520 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
521 				  CGM_METAFILE_DESCRIPTOR_ELEMENT, 11,
522 				  data_len, &byte_count,
523 				  "MFELEMLIST");
524 	switch (_plotter->cgm_encoding)
525 	  {
526 	  case CGM_ENCODING_BINARY:
527 	  default:
528 	    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
529 			       length,
530 			       data_len, &data_byte_count, &byte_count);
531 	    for (k = 0; k < length; k++)
532 	      {
533 		_cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
534 				 element_list->class_id[k],
535 				 data_len, &data_byte_count, &byte_count);
536 		_cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
537 				 element_list->element_id[k],
538 				 data_len, &data_byte_count, &byte_count);
539 	      }
540 	    break;
541 	  case CGM_ENCODING_CHARACTER: /* not supported */
542 	    break;
543 
544 	  case CGM_ENCODING_CLEAR_TEXT:
545 	    _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
546 			      element_list->text_string,
547 			      (int) strlen (element_list->text_string),
548 			      true,
549 			      data_len, &data_byte_count, &byte_count);
550 	    break;
551 	  }
552 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
553 				      &byte_count);
554       }
555 
556       /* emit "METAFILE DESCRIPTION" command, including profile string etc. */
557       {
558 	time_t clock;
559 	const char *profile_string, *profile_edition_string;
560 	char string_param[254];
561 	struct tm *local_time_struct_ptr;
562 #ifdef HAVE_LOCALTIME_R
563 	struct tm local_time_struct;
564 #endif
565 
566 	/* Work out ASCII specification of profile */
567 	switch (_plotter->cgm_profile)
568 	  {
569 	  case CGM_PROFILE_WEB:
570 	    profile_string = "WebCGM";
571 	    profile_edition_string = "1.0";
572 	    break;
573 	  case CGM_PROFILE_MODEL:
574 	    profile_string = "Model-Profile";
575 	    profile_edition_string = "1";
576 	    break;
577 	  case CGM_PROFILE_NONE:
578 	  default:
579 	    profile_string = "None";
580 	    profile_edition_string = "0.0"; /* waggish */
581 	    break;
582 	  }
583 
584 	/* Compute an ASCII representation of the current time, in a
585 	   reentrant way if we're supporting pthreads (i.e. by using
586 	   localtime_r if it's available). */
587 	time (&clock);
588 
589 #ifdef PTHREAD_SUPPORT
590 #ifdef HAVE_PTHREAD_H
591 #ifdef HAVE_LOCALTIME_R
592 	localtime_r (&clock, &local_time_struct);
593 	local_time_struct_ptr = &local_time_struct;
594 #else
595 	local_time_struct_ptr = localtime (&clock);
596 #endif
597 #else  /* not HAVE_PTHREAD_H */
598 	local_time_struct_ptr = localtime (&clock);
599 #endif /* not HAVE_PTHREAD_H */
600 #else  /* not PTHREAD_SUPPORT */
601 	local_time_struct_ptr = localtime (&clock);
602 #endif /* not PTHREAD_SUPPORT */
603 
604 	sprintf (string_param,
605 		 "\"ProfileId:%s\" \"ProfileEd:%s\" \"ColourClass:%s\" \"Source:GNU libplot %s\" \"Date:%04d%02d%02d\"",
606 		 profile_string, profile_edition_string,
607 		 _plotter->cgm_need_color ? "colour" : "monochrome",
608 		 PL_LIBPLOT_VER_STRING,
609 		 1900 + local_time_struct_ptr->tm_year,
610 		 1 + local_time_struct_ptr->tm_mon,
611 		 local_time_struct_ptr->tm_mday);
612 
613 	string_length = strlen (string_param);
614 	data_len = CGM_BINARY_BYTES_PER_STRING(string_length);
615 	byte_count = data_byte_count = 0;
616 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
617 				  CGM_METAFILE_DESCRIPTOR_ELEMENT, 2,
618 				  data_len, &byte_count,
619 				  "MFDESC");
620 	_cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
621 			  string_param,
622 			  string_length, false,	/* delimit by single quotes */
623 			  data_len, &data_byte_count, &byte_count);
624 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
625 				      &byte_count);
626       }
627 
628       /* emit "VDC TYPE" command, selecting integer VDC's for the metafile */
629       {
630 	data_len = 2;		/* 2 bytes per enum */
631 	byte_count = data_byte_count = 0;
632 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
633 				  CGM_METAFILE_DESCRIPTOR_ELEMENT, 3,
634 				  data_len, &byte_count,
635 				  "VDCTYPE");
636 	_cgm_emit_enum (doc_header, false, _plotter->cgm_encoding,
637 			0,
638 			data_len, &data_byte_count, &byte_count,
639 			"integer");
640 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
641 				      &byte_count);
642       }
643 
644       /* Emit "INTEGER PRECISION" command.  Parameters are
645 	 encoding-dependent: in the binary encoding, the number of bits k,
646 	 and in clear text, a pair of integers: the minimum and maximum
647 	 integers representable in CGM format, i.e. -(2^(k-1) - 1) and
648 	 (2^(k-1) - 1), where k=8*CGM_BINARY_BYTES_PER_INTEGER. */
649       {
650 	int j, max_int;
651 
652 	data_len = 2;		/* in binary, 16 bits of data; see comment */
653 	byte_count = data_byte_count = 0;
654 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
655 				  CGM_METAFILE_DESCRIPTOR_ELEMENT, 4,
656 				  data_len, &byte_count,
657 				  "INTEGERPREC");
658 	switch (_plotter->cgm_encoding)
659 	  {
660 	  case CGM_ENCODING_BINARY:
661 	  default:
662 
663 	    /* The integer precision, in terms of bits, should be encoded
664 	       as an integer at the current precision (the default, which
665 	       is 16 bits), not the eventual precision.  So we don't call
666 	       _cgm_emit_integer; we call _cgm_emit_index instead.  We
667 	       always represent indices by 16 bits (the default). */
668 
669 	    _cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
670 			     8 * CGM_BINARY_BYTES_PER_INTEGER,
671 			     data_len, &data_byte_count, &byte_count);
672 	    break;
673 	  case CGM_ENCODING_CHARACTER: /* not supported */
674 	    break;
675 
676 	  case CGM_ENCODING_CLEAR_TEXT:
677 	    max_int = 0;
678 	    for (j = 0; j < (8 * CGM_BINARY_BYTES_PER_INTEGER - 1); j++)
679 	      max_int += (1 << j);
680 	    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
681 			       -max_int,
682 			       data_len, &data_byte_count, &byte_count);
683 	    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
684 			       max_int,
685 			       data_len, &data_byte_count, &byte_count);
686 	    break;
687 	  }
688 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
689 				      &byte_count);
690       }
691 
692       /* Emit "REAL PRECISION" command, selecting default precision.
693 
694 	 Parameters are encoding-dependent.  In the clear text encoding,
695 	 three numbers: the minimum real, the maximum real, and the number
696 	 of significant decimal digits (an integer).  Typical choices are
697 	 (-32767.0, 32767.0, 4) [the default].  In the binary encoding,
698 	 three objects: a 2-octet enumerative specifying the encoding of
699 	 reals (0=floating, 1=fixed), and two integers at current intege
700 	 precision specifying the size of each piece of the encoded real.
701 	 Typical choices are (1,16,16) [the default] or (0,9,23). */
702       {
703 	data_len = 2 + 2 * CGM_BINARY_BYTES_PER_INTEGER;
704 	byte_count = data_byte_count = 0;
705 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
706 				  CGM_METAFILE_DESCRIPTOR_ELEMENT, 5,
707 				  data_len, &byte_count,
708 				  "REALPREC");
709 	switch (_plotter->cgm_encoding)
710 	  {
711 	  case CGM_ENCODING_BINARY:
712 	  default:
713 	    _cgm_emit_enum (doc_header, false, _plotter->cgm_encoding,
714 			    1,
715 			    data_len, &data_byte_count, &byte_count,
716 			    "DUMMY");
717 	    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
718 			       16,
719 			       data_len, &data_byte_count, &byte_count);
720 	    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
721 			       16,
722 			       data_len, &data_byte_count, &byte_count);
723 	    break;
724 	  case CGM_ENCODING_CHARACTER: /* not supported */
725 	    break;
726 
727 	  case CGM_ENCODING_CLEAR_TEXT:
728 	    _cgm_emit_real_fixed_point (doc_header, false, _plotter->cgm_encoding,
729 					-32767.0,
730 					data_len, &data_byte_count, &byte_count);
731 	    _cgm_emit_real_fixed_point (doc_header, false, _plotter->cgm_encoding,
732 					32767.0,
733 					data_len, &data_byte_count, &byte_count);
734 	    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
735 			       4,
736 			       data_len, &data_byte_count, &byte_count);
737 	    break;
738 	  }
739 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
740 				      &byte_count);
741       }
742 
743       /* Emit "COLOR PRECISION" command.  Parameters are
744 	 encoding-dependent: in the binary encoding, an integer specifying
745 	 the number of bits k, and in clear text, the maximum possible
746 	 color component value, i.e. (2^k - 1), where
747 	 k=8*CGM_BINARY_BYTES_PER_COLOR_COMPONENT. */
748       {
749 	int j;
750 	unsigned int max_component;
751 
752 	data_len = CGM_BINARY_BYTES_PER_INTEGER;
753 	byte_count = data_byte_count = 0;
754 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
755 				  CGM_METAFILE_DESCRIPTOR_ELEMENT, 7,
756 				  data_len, &byte_count,
757 				  "COLRPREC");
758 	switch (_plotter->cgm_encoding)
759 	  {
760 	  case CGM_ENCODING_BINARY:
761 	  default:
762 	    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
763 			       8 * CGM_BINARY_BYTES_PER_COLOR_COMPONENT,
764 			       data_len, &data_byte_count, &byte_count);
765 	    break;
766 	  case CGM_ENCODING_CHARACTER: /* not supported */
767 	    break;
768 
769 	  case CGM_ENCODING_CLEAR_TEXT:
770 	    max_component = 0;
771 	    for (j = 0; j < (8 * CGM_BINARY_BYTES_PER_COLOR_COMPONENT); j++)
772 	      max_component += (1 << j);
773 	    _cgm_emit_unsigned_integer (doc_header, false, _plotter->cgm_encoding,
774 					max_component,
775 					data_len, &data_byte_count, &byte_count);
776 	    break;
777 	  }
778 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
779 				      &byte_count);
780       }
781 
782       /* emit "COLOR VALUE EXTENT" command, duplicating the information
783 	 we just supplied (this is necessary) */
784       {
785 	int j;
786 	unsigned int max_component;
787 
788 	data_len = 6 * CGM_BINARY_BYTES_PER_COLOR_COMPONENT;
789 	byte_count = data_byte_count = 0;
790 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
791 				  CGM_METAFILE_DESCRIPTOR_ELEMENT, 10,
792 				  data_len, &byte_count,
793 				  "COLRVALUEEXT");
794 	_cgm_emit_color_component (doc_header, false, _plotter->cgm_encoding,
795 				   (unsigned int)0,
796 				   data_len, &data_byte_count, &byte_count);
797 	_cgm_emit_color_component (doc_header, false, _plotter->cgm_encoding,
798 				   (unsigned int)0,
799 				   data_len, &data_byte_count, &byte_count);
800 	_cgm_emit_color_component (doc_header, false, _plotter->cgm_encoding,
801 				   (unsigned int)0,
802 				   data_len, &data_byte_count, &byte_count);
803 	max_component = 0;
804 	for (j = 0; j < (8 * CGM_BINARY_BYTES_PER_COLOR_COMPONENT); j++)
805 	  max_component += (1 << j);
806 
807 	_cgm_emit_color_component (doc_header, false, _plotter->cgm_encoding,
808 				   max_component,
809 				   data_len, &data_byte_count, &byte_count);
810 	_cgm_emit_color_component (doc_header, false, _plotter->cgm_encoding,
811 				   max_component,
812 				   data_len, &data_byte_count, &byte_count);
813 	_cgm_emit_color_component (doc_header, false, _plotter->cgm_encoding,
814 				   max_component,
815 				   data_len, &data_byte_count, &byte_count);
816 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
817 				      &byte_count);
818       }
819 
820       /* determine fonts needed by document, by examining all pages */
821       {
822 	current_page = _plotter->data->first_page;
823 
824 	for (i = 0; i < PL_NUM_PS_FONTS; i++)
825 	  ps_font_used_in_doc[i] = false;
826 	while (current_page)
827 	  {
828 	    for (i = 0; i < PL_NUM_PS_FONTS; i++)
829 	      if (current_page->ps_font_used[i])
830 		ps_font_used_in_doc[i] = true;
831 	    current_page = current_page->next;
832 	  }
833       }
834 
835       /* Map our internal indexing of PS fonts to the indexing we use in a
836 	 CGM file (which may be different, because we want the traditional
837 	 `Adobe 13' to come first, out of the `Adobe 35').  Also work out
838 	 whether Symbol font, which has its own character sets, is used. */
839       symbol_font_used_in_doc = false;
840       for (i = 0; i < PL_NUM_PS_FONTS; i++)
841 	{
842 	  cgm_font_id_used_in_doc[_pl_g_ps_font_to_cgm_font_id[i]] = ps_font_used_in_doc[i];
843 	  if (ps_font_used_in_doc[i]
844 	      && strcmp (_pl_g_ps_font_info[i].ps_name, "Symbol") == 0)
845 	    symbol_font_used_in_doc = true;
846 	}
847 
848       /* compute maximum used font id, if any */
849       max_cgm_font_id = 0;
850       doc_uses_fonts = false;
851       for (i = 0; i < PL_NUM_PS_FONTS; i++)
852 	{
853 	  if (cgm_font_id_used_in_doc[i] == true)
854 	    {
855 	      doc_uses_fonts = true;
856 	      max_cgm_font_id = i;
857 	    }
858 	}
859 
860       if (doc_uses_fonts)
861 	{
862 	  /* emit "FONT LIST" command */
863 
864 	  /* command will include encoded strings, which are the names of
865 	     fonts in range 0..max_cgm_font_id; later in the CGM file,
866 	     they'll be referred to as 1..max_cgm_font_id+1 */
867 	  data_len = 0;
868 	  for (i = 0; i <= max_cgm_font_id; i++)
869 	    {
870 	      int ps_font_index;
871 	      int font_name_length, encoded_font_name_length;
872 
873 	      ps_font_index = _pl_g_cgm_font_id_to_ps_font[i];
874 	      font_name_length = (int) strlen (_pl_g_ps_font_info[ps_font_index].ps_name);
875 	      encoded_font_name_length =
876 		CGM_BINARY_BYTES_PER_STRING(font_name_length);
877 	      data_len += encoded_font_name_length;
878 	    }
879 	  byte_count = data_byte_count = 0;
880 
881 	  _cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
882 				    CGM_METAFILE_DESCRIPTOR_ELEMENT, 13,
883 				    data_len, &byte_count,
884 				    "FONTLIST");
885 	  for (i = 0; i <= max_cgm_font_id; i++)
886 	    {
887 	      int ps_font_index;
888 
889 	      ps_font_index = _pl_g_cgm_font_id_to_ps_font[i];
890 	      _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
891 				_pl_g_ps_font_info[ps_font_index].ps_name,
892 				(int) strlen (_pl_g_ps_font_info[ps_font_index].ps_name),
893 				true,
894 				data_len, &data_byte_count, &byte_count);
895 	    }
896 	  _cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
897 					&byte_count);
898 
899 	  if (_plotter->cgm_version >= 3)
900 	    /* emit version-3 "FONT PROPERTIES" commands; note that if
901 	       fonts are used and CGM_MAX_VERSION is >=3, then cgm_version
902 	       was previously bumped up to 3 in c_closepl.c */
903 	    {
904 	      /* For each font in the font list, we specify 7 properties.
905 		 Each "FONT PROPERTIES" command refers to a single font.
906 		 Its argument list is a sequence of 3-tuples, each being of
907 		 the form (property type [an index], priority [an integer],
908 		 value), where `value' is an SDR (structured data record).
909 		 One of the supported property types is
910 		 CGM_FONT_PROP_INDEX, the value of which is an index into
911 		 the font list.  For any invocation of the command, this
912 		 property type and its value must be supplied.
913 
914 		 An SDR is a string-encoded structure, and each `run' of a
915 		 single CGM datatype within an SDR is encoded as (A) an
916 		 identifier for the datatype [an index], (B) a count of the
917 		 number of occurrences [an integer], and (C) the
918 		 occurrences of the datatype, themselves.  So in the binary
919 		 encoding, bytes per SDR equals
920 		 2 + bytes_per_integer + data_bytes, since we always encode
921 		 CGM indices as 2 bytes.
922 
923 		 The only SDR's that occur in this context are
924 		 (1) single CGM indices [used for 5 of the 7 font properties],
925 		 (2) single CGM strings [used for the `family' property], and
926 		 (3) three 8-bit unsigned integers [used for the font's
927 		 `design group'].
928 
929 		 Because we always encode CGM indices as 2 bytes, bytes
930 		 per SDR, in the binary encoding, in these 3 cases are:
931 		 (1) 1+ CGM_BINARY_BYTES_PER_INTEGER + 4,
932 		 (2) 1+ CGM_BINARY_BYTES_PER_INTEGER + 2 + CGM_BYTES_PER_STRING
933 		 (3) 1+ CGM_BINARY_BYTES_PER_INTEGER + 5.
934 		 (This takes account of the initial byte used for the
935 		 string encoding of the SDR.)
936 
937 		 And bytes per 3-tuple in these three cases are:
938 		 (1) 2*CGM_BINARY_BYTES_PER_INTEGER + 7,
939 		 (2) 2*CGM_BINARY_BYTES_PER_INTEGER + 5 + CGM_BYTES_PER_STRING
940 		 (3) 2*CGM_BINARY_BYTES_PER_INTEGER + 8.
941 		 Since for every included font, we emit 5 3-tuples of type 1,
942 		 1 of type 2, and 1 of type 3, bytes per emitted font equals
943 		 14*CGM_BINARY_BYTES_PER_INTEGER + 48 + CGM_BYTES_PER_STRING().
944 
945 		 In the binary encoding, this is the length, in bytes, of
946 		 the argument list of each "FONT PROPERTIES" command.  Here
947 		 CGM_BYTES_PER_STRING() stands for the string-encoded
948 		 length of the family name of the font. */
949 
950 	      for (i = 0; i <= max_cgm_font_id; i++)
951 		{
952 		  int family_length;
953 		  plOutbuf *sdr_buffer;
954 
955 		  family_length = strlen(_pl_g_cgm_font_properties[i].family);
956 		  data_len = (14 * CGM_BINARY_BYTES_PER_INTEGER
957 			      + 48 /* hardcoded constants; see above */
958 			      + CGM_BINARY_BYTES_PER_STRING(family_length));
959 		  byte_count = data_byte_count = 0;
960 
961 		  sdr_buffer = _new_outbuf ();
962 		  _cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
963 					    CGM_METAFILE_DESCRIPTOR_ELEMENT, 21,
964 					    data_len, &byte_count,
965 					    "FONTPROP");
966 
967 		  /* now emit a sequence of 3-tuples: (index, integer, SDR);
968 		     for each 2nd element (a priority), we just specify `1' */
969 
970 		  /* specify index of font in table (beginning with 1, not
971                      with 0) */
972 		  {
973 		    _cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
974 				     CGM_FONT_PROP_INDEX,
975 				     data_len, &data_byte_count, &byte_count);
976 		    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
977 				       1, /* priority */
978 				       data_len, &data_byte_count, &byte_count);
979 		    build_sdr_from_index (sdr_buffer, _plotter->cgm_encoding,
980 					   i + 1); /* add 1 to index */
981 		    _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
982 				      sdr_buffer->base,
983 				      (int)(sdr_buffer->contents),
984 				      false,
985 				      data_len, &data_byte_count, &byte_count);
986 		    _reset_outbuf (sdr_buffer);
987 		  }
988 
989 		  /* specify font family */
990 		  {
991 		    _cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
992 				     CGM_FONT_PROP_FAMILY,
993 				     data_len, &data_byte_count, &byte_count);
994 		    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
995 				       1,
996 				       data_len, &data_byte_count, &byte_count);
997 		    build_sdr_from_string (sdr_buffer, _plotter->cgm_encoding,
998 					    _pl_g_cgm_font_properties[i].family,
999 					    (int)(strlen (_pl_g_cgm_font_properties[i].family)),
1000 					    true); /* use double quotes */
1001 		    _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
1002 				      sdr_buffer->base,
1003 				      (int)(sdr_buffer->contents),
1004 				      false,
1005 				      data_len, &data_byte_count, &byte_count);
1006 		    _reset_outbuf (sdr_buffer);
1007 		  }
1008 
1009 		  /* specify font posture */
1010 		  {
1011 		    _cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
1012 				     CGM_FONT_PROP_POSTURE,
1013 				     data_len, &data_byte_count, &byte_count);
1014 		    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
1015 				       1,
1016 				       data_len, &data_byte_count, &byte_count);
1017 		    build_sdr_from_index (sdr_buffer, _plotter->cgm_encoding,
1018 					   _pl_g_cgm_font_properties[i].posture);
1019 		    _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
1020 				      sdr_buffer->base,
1021 				      (int)(sdr_buffer->contents),
1022 				      false,
1023 				      data_len, &data_byte_count, &byte_count);
1024 		    _reset_outbuf (sdr_buffer);
1025 		  }
1026 
1027 		  /* specify font weight */
1028 		  {
1029 		    _cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
1030 				     CGM_FONT_PROP_WEIGHT,
1031 				     data_len, &data_byte_count, &byte_count);
1032 		    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
1033 				       1,
1034 				       data_len, &data_byte_count, &byte_count);
1035 		    build_sdr_from_index (sdr_buffer, _plotter->cgm_encoding,
1036 					   _pl_g_cgm_font_properties[i].weight);
1037 		    _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
1038 				      sdr_buffer->base,
1039 				      (int)(sdr_buffer->contents),
1040 				      false,
1041 				      data_len, &data_byte_count, &byte_count);
1042 		    _reset_outbuf (sdr_buffer);
1043 		  }
1044 
1045 		  /* specify font width */
1046 		  {
1047 		    _cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
1048 				     CGM_FONT_PROP_WIDTH,
1049 				     data_len, &data_byte_count, &byte_count);
1050 		    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
1051 				       1,
1052 				       data_len, &data_byte_count, &byte_count);
1053 		    build_sdr_from_index (sdr_buffer, _plotter->cgm_encoding,
1054 					   _pl_g_cgm_font_properties[i].proportionate_width);
1055 		    _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
1056 				      sdr_buffer->base,
1057 				      (int)(sdr_buffer->contents),
1058 				      false,
1059 				      data_len, &data_byte_count, &byte_count);
1060 		    _reset_outbuf (sdr_buffer);
1061 		  }
1062 
1063 		  /* specify font design group */
1064 		  {
1065 		    _cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
1066 				     CGM_FONT_PROP_DESIGN_GROUP,
1067 				     data_len, &data_byte_count, &byte_count);
1068 		    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
1069 				       1,
1070 				       data_len, &data_byte_count, &byte_count);
1071 		    build_sdr_from_ui8s (sdr_buffer, _plotter->cgm_encoding,
1072 					  _pl_g_cgm_font_properties[i].design_group,
1073 					  3);
1074 		    _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
1075 				      sdr_buffer->base,
1076 				      (int)(sdr_buffer->contents),
1077 				      false,
1078 				      data_len, &data_byte_count, &byte_count);
1079 		    _reset_outbuf (sdr_buffer);
1080 		  }
1081 
1082 		  /* specify font structure */
1083 		  {
1084 		    _cgm_emit_index (doc_header, false, _plotter->cgm_encoding,
1085 				     CGM_FONT_PROP_STRUCTURE,
1086 				     data_len, &data_byte_count, &byte_count);
1087 		    _cgm_emit_integer (doc_header, false, _plotter->cgm_encoding,
1088 				       1,
1089 				       data_len, &data_byte_count, &byte_count);
1090 		    build_sdr_from_index (sdr_buffer, _plotter->cgm_encoding,
1091 					   _pl_g_cgm_font_properties[i].structure);
1092 		    _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
1093 				      sdr_buffer->base,
1094 				      (int)(sdr_buffer->contents),
1095 				      false,
1096 				      data_len, &data_byte_count, &byte_count);
1097 		    _reset_outbuf (sdr_buffer);
1098 		  }
1099 
1100 		  _cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
1101 						&byte_count);
1102 		  _delete_outbuf (sdr_buffer);
1103 		}
1104 	    }
1105 
1106 	  /* Emit a "CHARACTER SET LIST" command.  Argument list is a
1107 	     sequence of character sets, with each character set being
1108 	     expressed both as a CGM enumerative and a CGM string (the
1109 	     `designation sequence tail').
1110 
1111 	     We include the 2 character sets used in the 8-bit ISO-Latin-1
1112 	     encoding, and, if we're using the Symbol font, the two
1113 	     character sets that Symbol uses, also.  So internally, we
1114 	     index these character sets by 1,2,3,4 (the latter two may not
1115 	     be present). */
1116 
1117 	  data_len = 0;
1118 	  for (i = 0; i < 2; i++)
1119 	    {
1120 	      int tail_length, encoded_tail_length;
1121 
1122 	      data_len += 2;	/* 2 bytes per enum */
1123 	      tail_length = strlen (_iso_latin_1_cgm_charset[i].tail);
1124 	      encoded_tail_length =
1125 		CGM_BINARY_BYTES_PER_STRING(tail_length);
1126 	      data_len += encoded_tail_length;
1127 	    }
1128 	  if (symbol_font_used_in_doc)
1129 	    for (i = 0; i < 2; i++)
1130 	      {
1131 		int tail_length, encoded_tail_length;
1132 
1133 		data_len += 2;	/* 2 bytes per enum */
1134 		tail_length = (int) strlen (_symbol_cgm_charset[i].tail);
1135 		encoded_tail_length =
1136 		  CGM_BINARY_BYTES_PER_STRING(tail_length);
1137 		data_len += encoded_tail_length;
1138 	      }
1139 	  byte_count = data_byte_count = 0;
1140 
1141 	  _cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
1142 				    CGM_METAFILE_DESCRIPTOR_ELEMENT, 14,
1143 				    data_len, &byte_count,
1144 				    "CHARSETLIST");
1145 	  for (i = 0; i < 2; i++)
1146 	    {
1147 	      _cgm_emit_enum (doc_header, false, _plotter->cgm_encoding,
1148 			      _iso_latin_1_cgm_charset[i].type,
1149 			      data_len, &data_byte_count, &byte_count,
1150 			      _iso_latin_1_cgm_charset[i].type_string);
1151 	      _cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
1152 				_iso_latin_1_cgm_charset[i].tail,
1153 				(int) strlen (_iso_latin_1_cgm_charset[i].tail),
1154 				true,
1155 				data_len, &data_byte_count, &byte_count);
1156 	    }
1157 	  if (symbol_font_used_in_doc)
1158 	    for (i = 0; i < 2; i++)
1159 	      {
1160 		_cgm_emit_enum (doc_header, false, _plotter->cgm_encoding,
1161 				_symbol_cgm_charset[i].type,
1162 				data_len, &data_byte_count, &byte_count,
1163 				_symbol_cgm_charset[i].type_string);
1164 		_cgm_emit_string (doc_header, false, _plotter->cgm_encoding,
1165   				  _symbol_cgm_charset[i].tail,
1166 				  (int) strlen (_symbol_cgm_charset[i].tail),
1167 				  true,
1168 				  data_len, &data_byte_count, &byte_count);
1169 	      }
1170 	  _cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
1171 					&byte_count);
1172 	}
1173 
1174       /* emit "CHARACTER CODING ANNOUNCER" command, selecting 8-bit
1175 	 character codes (no switching between font halves for us!) */
1176       {
1177 	data_len = 2; /* 2 bytes per enum */
1178 	byte_count = data_byte_count = 0;
1179 	_cgm_emit_command_header (doc_header, _plotter->cgm_encoding,
1180 				  CGM_METAFILE_DESCRIPTOR_ELEMENT, 15,
1181 				  data_len, &byte_count,
1182 				  "CHARCODING");
1183 	_cgm_emit_enum (doc_header, false, _plotter->cgm_encoding,
1184 			1,
1185 			data_len, &data_byte_count, &byte_count,
1186 			"basic8bit");
1187 	_cgm_emit_command_terminator (doc_header, _plotter->cgm_encoding,
1188 				      &byte_count);
1189       }
1190 
1191       /* WRITE DOCUMENT HEADER */
1192       _write_bytes (_plotter->data,
1193 			     (int)(doc_header->contents),
1194 			     (unsigned char *)doc_header->base);
1195       _delete_outbuf (doc_header);
1196 
1197       /* loop over plOutbufs in which successive pages of graphics are
1198 	 stored; emit each page as a CGM picture, and delete each plOutbuf
1199 	 as we finish with it */
1200       current_page = _plotter->data->first_page;
1201       i = 1;
1202 
1203       while (current_page)
1204 	{
1205 	  plOutbuf *next_page;
1206 	  plOutbuf *current_page_header, *current_page_trailer;
1207 
1208 	  /* prepare a page header */
1209 
1210 	  current_page_header = _new_outbuf ();
1211 
1212 	  /* emit "BEGIN PICTURE" command */
1213 	  {
1214 	    char picture[32];
1215 	    const char *string_param;
1216 
1217 	    sprintf (picture, "picture_%d", i);
1218 	    string_param = picture;
1219 	    string_length = strlen (string_param);
1220 	    data_len = CGM_BINARY_BYTES_PER_STRING(string_length);
1221 	    byte_count = data_byte_count = 0;
1222 	    _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1223 				      CGM_DELIMITER_ELEMENT, 3,
1224 				      data_len, &byte_count,
1225 				      "BEGPIC");
1226 	    _cgm_emit_string (current_page_header, false, _plotter->cgm_encoding,
1227 			      string_param,
1228 			      string_length,
1229 			      true,
1230 			      data_len, &data_byte_count, &byte_count);
1231 	    _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1232 					  &byte_count);
1233 	  }
1234 
1235 	  /* emit "VDC EXTENT" command [specify virtual device coor ranges] */
1236 	  {
1237 	    int imin_true, imax_true, jmin_true, jmax_true;
1238 
1239 	    data_len = 2 * 2 * 2;
1240 	    byte_count = data_byte_count = 0;
1241 	    _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1242 				      CGM_PICTURE_DESCRIPTOR_ELEMENT, 6,
1243 				      data_len, &byte_count,
1244 				      "VDCEXT");
1245 
1246 	    /* To see how we set extent values, see the initialize()
1247 	       routine.  In that routine, we choose device-coordinates
1248 	       ranges imin,imax,jmin,jmax so that the length of the longer
1249 	       side of the viewport is one-fourth the maximum integer
1250 	       range.  The other side will be reduced if the user specifies
1251 	       (via PAGESIZE) an aspect ratio other than 1:1.  With this
1252 	       scheme, if the user specifies xsize=ysize > 0, the same
1253 	       user->device coordinate map will result, no matter what
1254 	       xsize and ysize equal.
1255 
1256 	       However, if the user specifies (via PAGESIZE) a negative
1257 	       xsize or ysize, we flip the sign of imax-imin or jmax-jmin,
1258 	       thereby greatly modifying the user->device coordinate map.
1259 	       Which means we must flip it back, right here, before
1260 	       emitting the VDC extents.  The reason for this sign-flipping
1261 	       is that we don't trust CGM viewers to handle negative VDC
1262 	       extents. */
1263 
1264 	    if (_plotter->data->imax < _plotter->data->imin)
1265 	      {
1266 		imin_true = _plotter->data->imax;
1267 		imax_true = _plotter->data->imin;
1268 	      }
1269 	    else
1270 	      {
1271 		imin_true = _plotter->data->imin;
1272 		imax_true = _plotter->data->imax;
1273 	      }
1274 
1275 	    if (_plotter->data->jmax < _plotter->data->jmin)
1276 	      {
1277 		jmin_true = _plotter->data->jmax;
1278 		jmax_true = _plotter->data->jmin;
1279 	      }
1280 	    else
1281 	      {
1282 		jmin_true = _plotter->data->jmin;
1283 		jmax_true = _plotter->data->jmax;
1284 	      }
1285 
1286 	    /* In binary, we write each of these four coordinates as a
1287 	       16-bit `index' i.e. integer, because we haven't yet changed
1288 	       the VDC integer precision to our desired value (we can't do
1289 	       that until the beginning of the picture). */
1290 	    _cgm_emit_index (current_page_header, false, _plotter->cgm_encoding,
1291 			     imin_true,
1292 			     data_len, &data_byte_count, &byte_count);
1293 	    _cgm_emit_index (current_page_header, false, _plotter->cgm_encoding,
1294 			     jmin_true,
1295 			     data_len, &data_byte_count, &byte_count);
1296 	    _cgm_emit_index (current_page_header, false, _plotter->cgm_encoding,
1297 			     imax_true,
1298 			     data_len, &data_byte_count, &byte_count);
1299 	    _cgm_emit_index (current_page_header, false, _plotter->cgm_encoding,
1300 			     jmax_true,
1301 			     data_len, &data_byte_count, &byte_count);
1302 	    _cgm_emit_command_terminator (current_page_header,
1303 					  _plotter->cgm_encoding,
1304 					  &byte_count);
1305 	  }
1306 
1307 	  /* emit "SCALING MODE" command.  Specify metric scaling (required
1308 	   by WebCGM profile).  The argument is the number of millimeters
1309 	   per VDC unit; it must be a floating-point real.  */
1310 	  {
1311 	    int irange, jrange;
1312 	    double scaling_factor;
1313 
1314 	    data_len = 6;	/* 2 bytes per enum, 4 per floating-pt. real */
1315 	    byte_count = data_byte_count = 0;
1316 	    _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1317 				      CGM_PICTURE_DESCRIPTOR_ELEMENT, 1,
1318 				      data_len, &byte_count,
1319 				      "SCALEMODE");
1320 	    _cgm_emit_enum (current_page_header, false, _plotter->cgm_encoding,
1321 			     1,
1322 			     data_len, &data_byte_count, &byte_count,
1323 			     "metric");
1324 
1325 	    /* Compute a metric scaling factor from the criterion that the
1326 	       nominal physical width and height of VDC space be the
1327 	       viewport xsize and ysize, as determined by the PAGESIZE
1328 	       parameter.
1329 
1330 	       We can get this scaling factor easily, by computing a
1331 	       quotient, because our scheme for setting imin,imax,jmin,jmax
1332 	       in the initialize() method preserves aspect ratio, and
1333 	       signs, as well (see comment immediately above).  But we must
1334 	       be careful not to divide by zero, since zero-width and
1335 	       zero-height viewports are allowed. */
1336 
1337 	    irange = _plotter->data->imax - _plotter->data->imin;
1338 	    jrange = _plotter->data->jmax - _plotter->data->jmin;
1339 	    if (irange != 0)
1340 	      scaling_factor =
1341 		(25.4 * _plotter->data->viewport_xsize) / irange;
1342 	    else if (jrange != 0)
1343 	      scaling_factor =
1344 		(25.4 * _plotter->data->viewport_ysize) / jrange;
1345 	    else
1346 	      /* degenerate case, viewport has zero size */
1347 	      scaling_factor = 0.0;
1348 
1349 	    /* yes, this needs to be a floating-point real, not fixed-point! */
1350 	    _cgm_emit_real_floating_point (current_page_header, false, _plotter->cgm_encoding,
1351 					   scaling_factor,
1352 					   data_len, &data_byte_count, &byte_count);
1353 	    _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1354 					  &byte_count);
1355 	  }
1356 
1357 	  /* emit "LINE WIDTH SPECIFICATION MODE" command [specify
1358 	     absolute coordinates] */
1359 	  {
1360 	    data_len = 2;	/* 2 bytes per enum */
1361 	    byte_count = data_byte_count = 0;
1362 	    _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1363 				      CGM_PICTURE_DESCRIPTOR_ELEMENT, 3,
1364 				      data_len, &byte_count,
1365 				      "LINEWIDTHMODE");
1366 	    _cgm_emit_enum (current_page_header, false, _plotter->cgm_encoding,
1367 			     0,
1368 			     data_len, &data_byte_count, &byte_count,
1369 			     "abs");
1370 	    _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1371 					  &byte_count);
1372 	  }
1373 
1374 	  /* emit "EDGE WIDTH SPECIFICATION MODE" command [specify absolute
1375              coordinates] */
1376 	  {
1377 	    data_len = 2;	/* 2 bytes per enum */
1378 	    byte_count = data_byte_count = 0;
1379 	    _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1380 				      CGM_PICTURE_DESCRIPTOR_ELEMENT, 5,
1381 				      data_len, &byte_count,
1382 				      "EDGEWIDTHMODE");
1383 	    _cgm_emit_enum (current_page_header, false, _plotter->cgm_encoding,
1384 			     0,
1385 			     data_len, &data_byte_count, &byte_count,
1386 			     "abs");
1387 	    _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1388 					  &byte_count);
1389 	  }
1390 
1391 	  /* emit "MARKER SIZE SPECIFICATION MODE" command [specify
1392              absolute coordinates] */
1393 	  {
1394 	    data_len = 2;	/* 2 bytes per enum */
1395 	    byte_count = data_byte_count = 0;
1396 	    _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1397 				      CGM_PICTURE_DESCRIPTOR_ELEMENT, 4,
1398 				      data_len, &byte_count,
1399 				      "MARKERSIZEMODE");
1400 	    _cgm_emit_enum (current_page_header, false, _plotter->cgm_encoding,
1401 			     0,
1402 			     data_len, &data_byte_count, &byte_count,
1403 			     "abs");
1404 	    _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1405 					  &byte_count);
1406 	  }
1407 
1408 	  /* emit "COLOR SELECTION MODE" command [specify direct color,
1409 	     not indexed color] */
1410 	  {
1411 	    data_len = 2;	/* 2 bytes per enum */
1412 	    byte_count = data_byte_count = 0;
1413 	    _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1414 				      CGM_PICTURE_DESCRIPTOR_ELEMENT, 2,
1415 				      data_len, &byte_count,
1416 				      "COLRMODE");
1417 	    _cgm_emit_enum (current_page_header, false, _plotter->cgm_encoding,
1418 			     1,
1419 			     data_len, &data_byte_count, &byte_count,
1420 			     "direct");
1421 	    _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1422 					  &byte_count);
1423 	  }
1424 
1425 	  if (current_page->bg_color_suppressed == false)
1426 	    /* user didn't specify "none" as background color, so emit
1427 	       "BACKGROUND COLOR" command.  (Note that in a CGM file,
1428 	       background color is always a direct color specified by color
1429 	       components, never an indexed color.)  The background color
1430 	       for any page is stored in the `bg_color' element of its
1431 	       plOutbuf at the time the page is closed; see g_closepl.c.  */
1432 	    {
1433 	      data_len = 3 * CGM_BINARY_BYTES_PER_COLOR_COMPONENT;
1434 	      byte_count = data_byte_count = 0;
1435 	      _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1436 					CGM_PICTURE_DESCRIPTOR_ELEMENT, 7,
1437 					data_len, &byte_count,
1438 					"BACKCOLR");
1439 	      _cgm_emit_color_component (current_page_header, false, _plotter->cgm_encoding,
1440 					 (unsigned int)current_page->bg_color.red,
1441 					 data_len, &data_byte_count, &byte_count);
1442 	      _cgm_emit_color_component (current_page_header, false, _plotter->cgm_encoding,
1443 					 (unsigned int)current_page->bg_color.green,
1444 					 data_len, &data_byte_count, &byte_count);
1445 	      _cgm_emit_color_component (current_page_header, false, _plotter->cgm_encoding,
1446 					 (unsigned int)current_page->bg_color.blue,
1447 					 data_len, &data_byte_count, &byte_count);
1448 	      _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1449 					    &byte_count);
1450 	    }
1451 
1452 	  /* if user defined any line types, emit a sequence of "LINE AND
1453              EDGE TYPE DEFINITION" commands */
1454 	  {
1455 	    plCGMCustomLineType *linetype_ptr = (plCGMCustomLineType *)current_page->extra;
1456 	    int linetype = 0;
1457 
1458 	    while (linetype_ptr)
1459 	      {
1460 		int k, cycle_length, dash_array_len, *dash_array;
1461 
1462 		linetype--;	/* user-defined ones are -1,-2,-3,... */
1463 		dash_array_len = linetype_ptr->dash_array_len;
1464 		dash_array = linetype_ptr->dashes;
1465 		cycle_length = 0;
1466 		for (k = 0; k < dash_array_len; k++)
1467 		  cycle_length += dash_array[k];
1468 
1469 		/* data: a 2-byte index, the cycle length, and the array of
1470 		   dash lengths (all integers) */
1471 		data_len = 2 + (1 + dash_array_len) * CGM_BINARY_BYTES_PER_INTEGER;
1472 		byte_count = data_byte_count = 0;
1473 		_cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1474 					  CGM_PICTURE_DESCRIPTOR_ELEMENT, 17,
1475 					  data_len, &byte_count,
1476 					  "LINEEDGETYPEDEF");
1477 		_cgm_emit_index (current_page_header, false, _plotter->cgm_encoding,
1478 				 linetype,
1479 				 data_len, &data_byte_count, &byte_count);
1480 		_cgm_emit_integer (current_page_header, false, _plotter->cgm_encoding,
1481 				   cycle_length,
1482 				   data_len, &data_byte_count, &byte_count);
1483 		for (k = 0; k < dash_array_len; k++)
1484 		  _cgm_emit_integer (current_page_header, false, _plotter->cgm_encoding,
1485 				     dash_array[k],
1486 				     data_len, &data_byte_count, &byte_count);
1487 		_cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1488 					      &byte_count);
1489 
1490 		/* on to next user-defined line type */
1491 		linetype_ptr = linetype_ptr->next;
1492 	      }
1493 	  }
1494 
1495 	  /* emit "BEGIN PICTURE BODY" command */
1496 	  {
1497 	    data_len = 0;
1498 	    byte_count = data_byte_count = 0;
1499 	    _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1500 				      CGM_DELIMITER_ELEMENT, 4,
1501 				      data_len, &byte_count,
1502 				      "BEGPICBODY");
1503 	    _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1504 					  &byte_count);
1505 	  }
1506 
1507 	  /* Emit "VDC INTEGER PRECISION" command.  Very similar to the
1508 	     "INTEGER PRECISION" command, except we emit this at the start
1509 	     of each picture. */
1510 	  {
1511 	    int j, max_int;
1512 
1513 	    data_len = CGM_BINARY_BYTES_PER_INTEGER;
1514 	    byte_count = data_byte_count = 0;
1515 	    _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1516 				      CGM_CONTROL_ELEMENT, 1,
1517 				      data_len, &byte_count,
1518 				      "VDCINTEGERPREC");
1519 	    switch (_plotter->cgm_encoding)
1520 	      {
1521 	      case CGM_ENCODING_BINARY:
1522 	      default:
1523 		_cgm_emit_integer (current_page_header, false, _plotter->cgm_encoding,
1524 				   8 * CGM_BINARY_BYTES_PER_INTEGER,
1525 				   data_len, &data_byte_count, &byte_count);
1526 		break;
1527 	      case CGM_ENCODING_CHARACTER: /* not supported */
1528 		break;
1529 
1530 	      case CGM_ENCODING_CLEAR_TEXT:
1531 		max_int = 0;
1532 		for (j = 0; j < (8 * CGM_BINARY_BYTES_PER_INTEGER - 1); j++)
1533 		  max_int += (1 << j);
1534 		_cgm_emit_integer (current_page_header, false, _plotter->cgm_encoding,
1535 				   -max_int,
1536 				   data_len, &data_byte_count, &byte_count);
1537 		_cgm_emit_integer (current_page_header, false, _plotter->cgm_encoding,
1538 				   max_int,
1539 				   data_len, &data_byte_count, &byte_count);
1540 		break;
1541 	      }
1542 	    _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1543 					  &byte_count);
1544 	  }
1545 
1546 	  if (doc_uses_fonts)
1547 	    /* emit "TEXT PRECISION" command */
1548 	    {
1549 	      data_len = 2;	/* 2 bytes per enum */
1550 	      byte_count = data_byte_count = 0;
1551 	      _cgm_emit_command_header (current_page_header, _plotter->cgm_encoding,
1552 					CGM_ATTRIBUTE_ELEMENT, 11,
1553 					data_len, &byte_count,
1554 					"TEXTPREC");
1555 	      _cgm_emit_enum (current_page_header, false, _plotter->cgm_encoding,
1556 			      2,
1557 			      data_len, &data_byte_count, &byte_count,
1558 			      "stroke");
1559 	      _cgm_emit_command_terminator (current_page_header, _plotter->cgm_encoding,
1560 					    &byte_count);
1561 	    }
1562 
1563 	  /* write the page header */
1564 	  _write_bytes (_plotter->data,
1565 				 (int)(current_page_header->contents),
1566 				 (unsigned char *)current_page_header->base);
1567 	  _delete_outbuf (current_page_header);
1568 
1569 	  /* WRITE THE PICTURE */
1570 	  _write_bytes (_plotter->data,
1571 				 (int)(current_page->contents),
1572 				 (unsigned char *)current_page->base);
1573 
1574 	  /* prepare a page trailer */
1575 
1576 	  current_page_trailer = _new_outbuf ();
1577 
1578 	  /* emit "END PICTURE" command (no parameters) */
1579 	  {
1580 	    data_len = 0;
1581 	    byte_count = data_byte_count = 0;
1582 	    _cgm_emit_command_header (current_page_trailer, _plotter->cgm_encoding,
1583 				      CGM_DELIMITER_ELEMENT, 5,
1584 				      data_len, &byte_count,
1585 				      "ENDPIC");
1586 	    _cgm_emit_command_terminator (current_page_trailer, _plotter->cgm_encoding,
1587 					  &byte_count);
1588 	  }
1589 
1590 	  /* write page trailer */
1591 	  _write_bytes (_plotter->data,
1592 				 (int)(current_page_trailer->contents),
1593 				 (unsigned char *)current_page_trailer->base);
1594 	  _delete_outbuf (current_page_trailer);
1595 
1596 	  /* on to next page (if any) */
1597 	  next_page = current_page->next;
1598 	  current_page = next_page;
1599 	  i++;
1600 	}
1601 
1602       /* prepare a document trailer */
1603 
1604       doc_trailer = _new_outbuf ();
1605 
1606       /* emit "END METAFILE" command (no parameters) */
1607       {
1608 	data_len = 0;
1609 	byte_count = data_byte_count = 0;
1610 	_cgm_emit_command_header (doc_trailer, _plotter->cgm_encoding,
1611 				  CGM_DELIMITER_ELEMENT, 2,
1612 				  data_len, &byte_count,
1613 				  "ENDMF");
1614 	_cgm_emit_command_terminator (doc_trailer, _plotter->cgm_encoding,
1615 				      &byte_count);
1616       }
1617 
1618       /* WRITE DOCUMENT TRAILER */
1619       _write_bytes (_plotter->data,
1620 			     (int)(doc_trailer->contents),
1621 			     (unsigned char *)doc_trailer->base);
1622       _delete_outbuf (doc_trailer);
1623 
1624     }
1625 
1626   /* delete all plOutbufs in which document pages are stored */
1627   current_page = _plotter->data->first_page;
1628   while (current_page)
1629     {
1630       plOutbuf *next_page;
1631 
1632       next_page = current_page->next;
1633 
1634       /* deallocate page-specific table of user-specified line types,
1635 	 if any */
1636       if (current_page->extra)
1637 	{
1638 	  plCGMCustomLineType *linetype_ptr = (plCGMCustomLineType *)current_page->extra;
1639 	  plCGMCustomLineType *old_linetype_ptr;
1640 
1641 	  while (linetype_ptr)
1642 	    {
1643 	      if (linetype_ptr->dash_array_len > 0 /* paranoia */
1644 		  && linetype_ptr->dashes)
1645 		free (linetype_ptr->dashes);
1646 	      old_linetype_ptr = linetype_ptr;
1647 	      linetype_ptr = linetype_ptr->next;
1648 	      free (old_linetype_ptr);
1649 	    }
1650 	  _plotter->data->page->extra = (void *)NULL;
1651 	}
1652 
1653       _delete_outbuf (current_page);
1654       current_page = next_page;
1655     }
1656 
1657   /* flush output stream if any */
1658   if (_plotter->data->outfp)
1659     {
1660       if (fflush(_plotter->data->outfp) < 0
1661 #ifdef MSDOS
1662 	  /* data can be caught in DOS buffers, so do an fsync() too */
1663 	  || fsync (_plotter->data->outfp) < 0
1664 #endif
1665 	  )
1666 	_plotter->error (R___(_plotter) "the output stream is jammed");
1667     }
1668 #ifdef LIBPLOTTER
1669   else if (_plotter->data->outstream)
1670     {
1671       _plotter->data->outstream->flush ();
1672       if (!(*(_plotter->data->outstream)))
1673 	_plotter->error (R___(_plotter) "the output stream is jammed");
1674     }
1675 #endif
1676 
1677 #ifndef LIBPLOTTER
1678   /* in libplot, manually invoke superclass termination method */
1679   _pl_g_terminate (S___(_plotter));
1680 #endif
1681 }
1682 
1683 static void
build_sdr_from_index(plOutbuf * sdr_buffer,int cgm_encoding,int x)1684 build_sdr_from_index (plOutbuf *sdr_buffer, int cgm_encoding, int x)
1685 {
1686   int dummy_data_len, dummy_data_byte_count, dummy_byte_count;
1687 
1688   dummy_data_len = dummy_data_byte_count = dummy_byte_count = 0;
1689   _cgm_emit_index (sdr_buffer, true, cgm_encoding,
1690 		   CGM_SDR_DATATYPE_INDEX,
1691 		   dummy_data_len, &dummy_data_byte_count,
1692 		   &dummy_byte_count);
1693   _cgm_emit_integer (sdr_buffer, true, cgm_encoding,
1694 		     1,
1695 		     dummy_data_len, &dummy_data_byte_count,
1696 		     &dummy_byte_count);
1697   _cgm_emit_index (sdr_buffer, true, cgm_encoding,
1698 		   x,
1699 		   dummy_data_len, &dummy_data_byte_count,
1700 		   &dummy_byte_count);
1701 }
1702 
1703 static void
build_sdr_from_string(plOutbuf * sdr_buffer,int cgm_encoding,const char * s,int string_length,bool use_double_quotes)1704 build_sdr_from_string (plOutbuf *sdr_buffer, int cgm_encoding, const char *s, int string_length, bool use_double_quotes)
1705 {
1706   int dummy_data_len, dummy_data_byte_count, dummy_byte_count;
1707 
1708   dummy_data_len = dummy_data_byte_count = dummy_byte_count = 0;
1709   _cgm_emit_index (sdr_buffer, true, cgm_encoding,
1710 		   CGM_SDR_DATATYPE_STRING_FIXED,
1711 		   dummy_data_len, &dummy_data_byte_count,
1712 		   &dummy_byte_count);
1713   _cgm_emit_integer (sdr_buffer, true, cgm_encoding,
1714 		     1,
1715 		     dummy_data_len, &dummy_data_byte_count,
1716 		     &dummy_byte_count);
1717   _cgm_emit_string (sdr_buffer, true, cgm_encoding,
1718 		    s, string_length, use_double_quotes,
1719 		    dummy_data_len, &dummy_data_byte_count,
1720 		    &dummy_byte_count);
1721 }
1722 
1723 static void
build_sdr_from_ui8s(plOutbuf * sdr_buffer,int cgm_encoding,const int * x,int n)1724 build_sdr_from_ui8s (plOutbuf *sdr_buffer, int cgm_encoding, const int *x, int n)
1725 {
1726   int i, dummy_data_len, dummy_data_byte_count, dummy_byte_count;
1727 
1728   dummy_data_len = dummy_data_byte_count = dummy_byte_count = 0;
1729   _cgm_emit_index (sdr_buffer, true, cgm_encoding,
1730 		   CGM_SDR_DATATYPE_UNSIGNED_INTEGER_8BIT,
1731 		   dummy_data_len, &dummy_data_byte_count,
1732 		   &dummy_byte_count);
1733   _cgm_emit_integer (sdr_buffer, true, cgm_encoding,
1734 		     n,
1735 		     dummy_data_len, &dummy_data_byte_count,
1736 		     &dummy_byte_count);
1737   for (i = 0; i < n; i++)
1738     _cgm_emit_unsigned_integer_8bit (sdr_buffer, true, cgm_encoding,
1739 				     (unsigned int)(x[i]),
1740 				     dummy_data_len, &dummy_data_byte_count,
1741 				     &dummy_byte_count);
1742 }
1743 
1744 #ifdef LIBPLOTTER
CGMPlotter(FILE * infile,FILE * outfile,FILE * errfile)1745 CGMPlotter::CGMPlotter (FILE *infile, FILE *outfile, FILE *errfile)
1746 	:Plotter (infile, outfile, errfile)
1747 {
1748   _pl_c_initialize ();
1749 }
1750 
CGMPlotter(FILE * outfile)1751 CGMPlotter::CGMPlotter (FILE *outfile)
1752 	:Plotter (outfile)
1753 {
1754   _pl_c_initialize ();
1755 }
1756 
CGMPlotter(istream & in,ostream & out,ostream & err)1757 CGMPlotter::CGMPlotter (istream& in, ostream& out, ostream& err)
1758 	: Plotter (in, out, err)
1759 {
1760   _pl_c_initialize ();
1761 }
1762 
CGMPlotter(ostream & out)1763 CGMPlotter::CGMPlotter (ostream& out)
1764 	: Plotter (out)
1765 {
1766   _pl_c_initialize ();
1767 }
1768 
CGMPlotter()1769 CGMPlotter::CGMPlotter ()
1770 {
1771   _pl_c_initialize ();
1772 }
1773 
CGMPlotter(FILE * infile,FILE * outfile,FILE * errfile,PlotterParams & parameters)1774 CGMPlotter::CGMPlotter (FILE *infile, FILE *outfile, FILE *errfile, PlotterParams &parameters)
1775 	:Plotter (infile, outfile, errfile, parameters)
1776 {
1777   _pl_c_initialize ();
1778 }
1779 
CGMPlotter(FILE * outfile,PlotterParams & parameters)1780 CGMPlotter::CGMPlotter (FILE *outfile, PlotterParams &parameters)
1781 	:Plotter (outfile, parameters)
1782 {
1783   _pl_c_initialize ();
1784 }
1785 
CGMPlotter(istream & in,ostream & out,ostream & err,PlotterParams & parameters)1786 CGMPlotter::CGMPlotter (istream& in, ostream& out, ostream& err, PlotterParams &parameters)
1787 	: Plotter (in, out, err, parameters)
1788 {
1789   _pl_c_initialize ();
1790 }
1791 
CGMPlotter(ostream & out,PlotterParams & parameters)1792 CGMPlotter::CGMPlotter (ostream& out, PlotterParams &parameters)
1793 	: Plotter (out, parameters)
1794 {
1795   _pl_c_initialize ();
1796 }
1797 
CGMPlotter(PlotterParams & parameters)1798 CGMPlotter::CGMPlotter (PlotterParams &parameters)
1799 	: Plotter (parameters)
1800 {
1801   _pl_c_initialize ();
1802 }
1803 
~CGMPlotter()1804 CGMPlotter::~CGMPlotter ()
1805 {
1806   /* if luser left the Plotter open, close it */
1807   if (_plotter->data->open)
1808     _API_closepl ();
1809 
1810   _pl_c_terminate ();
1811 }
1812 #endif
1813