1 // This is core/vul/vul_psfile.cxx
2 #include <cmath>
3 #include <iostream>
4 #include <iomanip>
5 #include <algorithm>
6 #include "vul_psfile.h"
7 //:
8 // \file
9 
10 #ifdef _MSC_VER
11 #  include "vcl_msvc_warnings.h"
12 #endif
13 #include <cassert>
14 
15 #define RANGE(a, b, c)                                                                                                 \
16   {                                                                                                                    \
17     if ((a) < (b))                                                                                                     \
18       (a) = b;                                                                                                         \
19     if ((a) > (c))                                                                                                     \
20       (a) = c;                                                                                                         \
21   }
22 #define in_range(a) ((a) < 0x100)
23 #define Hex4bit(a) ((char)(((a) <= 9) ? ((a) + '0') : ((a)-10 + 'a')))
24 
25 static const float PIX2INCH = 72.0f;
26 static bool debug = true;
27 
28 // sizes of pages in inches
29 static double paper_size[8][2] = {
30   { 8.500, 11.000 },  // US NORMAL
31   { 8.268, 11.693 },  // A4 210mm x 297mm
32   { 7.205, 10.118 },  // B5 183mm x 257mm
33   { 11.693, 16.535 }, // A3 297mm x 420mm
34   { 8.500, 14.000 },  // US LEGAL
35   { 11.000, 17.000 }, // B-size
36   { 3.875, 4.875 },   // 4 by 5
37   { 0.945, 1.417 }    // 35mm (24x36)
38 };
39 
40 // size of l+r margin and t+b margin.  image is centered
41 static double margins[8][2] = {
42   { 1.000, 1.000 }, // US NORMAL
43   { 1.000, 1.000 }, // A4
44   { 1.000, 1.000 }, // B5
45   { 1.000, 1.000 }, // A3
46   { 1.000, 1.000 }, // US LEGAL
47   { 1.000, 1.000 }, // B-size
48   { 0.275, 0.275 }, // 4 by 5
49   { 0.078, 0.078 }  // 35mm (24x36)
50 };
51 
52 // min and max value for PostScript paper size
53 static float ps_minimum = 0.1f;
54 static float ps_maximum = 8.f;
55 
56 static const std::streampos HEADER_START(-1);
57 
58 //-----------------------------------------------------------------------------
59 //: Default constructor.
60 //-----------------------------------------------------------------------------
vul_psfile(char const * f,bool dbg)61 vul_psfile::vul_psfile(char const * f, bool dbg)
62   : output_filestream(f)
63   , fg_r(0)
64   , fg_g(0)
65   , fg_b(0)
66   , bg_r(1)
67   , bg_g(1)
68   , bg_b(1)
69   , line_width_(1)
70   , scale_x(1.f)
71   , scale_y(1.f)
72   , ox(0)
73   , oy(0)
74   , iw(0)
75   , ih(0)
76   , iwf(1.0)
77   , ihf(1.0)
78   , psizex(8.5)
79   , psizey(11)
80   , pos_inx(4.25)
81   , pos_iny(5.5)
82   , width(0)
83   , height(0)
84   , filename(f)
85   , printer_paper_type(vul_psfile::US_NORMAL)
86   , printer_paper_orientation(vul_psfile::PORTRAIT)
87   , printer_paper_layout(vul_psfile::CENTER)
88   , reduction_factor(1)
89   , doneps(false)
90   , min_x(1000)
91   , min_y(1000)
92   , max_x(-1000)
93   , max_y(-1000)
94   , box_width(0)
95   , box_height(0)
96   , translate_pos(-1L)
97   , sobj_t_pos(-1L)
98   , header_pos(HEADER_START)
99   , graphics_prolog_exists(false)
100   , exist_image(false)
101   , exist_objs(false)
102 {
103   debug = dbg;
104   if (debug)
105     std::cout << "vul_psfile::vul_psfile\n";
106   postscript_header();
107 }
108 
109 //-----------------------------------------------------------------------------
110 //: Destructor
111 //-----------------------------------------------------------------------------
~vul_psfile()112 vul_psfile::~vul_psfile()
113 {
114   if (debug)
115     std::cout << "vul_psfile::~vul_psfile\n";
116   reset_bounding_box();
117   if (!doneps)
118     done();
119 }
120 
121 
122 //-----------------------------------------------------------------------------
123 //: Rewrite output bounding box parameters.
124 //-----------------------------------------------------------------------------
125 void
reset_bounding_box()126 vul_psfile::reset_bounding_box()
127 {
128   std::streampos temp_pos;
129   temp_pos = output_filestream.tellp();
130 
131   if (exist_image)
132   {
133     // for image part
134     output_filestream.seekp(translate_pos);
135     image_translate_and_scale();
136   }
137   if (exist_objs)
138   {
139     // For Object part.
140     output_filestream.seekp(sobj_t_pos);
141     object_translate_and_scale();
142   }
143 
144   // reset Bounding Box parameters (the fourth line).
145   output_filestream.seekp(header_pos);
146   reset_postscript_header();
147   output_filestream.seekp(temp_pos);
148 }
149 
150 //-----------------------------------------------------------------------------
151 //: Recalculate bounding box and scale x and y (if necessary).
152 //-----------------------------------------------------------------------------
153 void
compute_bounding_box()154 vul_psfile::compute_bounding_box()
155 {
156   box_width = max_x - min_x;
157   box_height = max_y - min_y;
158 
159   if (printer_paper_orientation == vul_psfile::LANDSCAPE)
160   {
161     psizex = paper_size[printer_paper_type][1];
162     psizey = paper_size[printer_paper_type][0];
163   }
164   else
165   {
166     psizex = paper_size[printer_paper_type][0];
167     psizey = paper_size[printer_paper_type][1];
168   }
169 
170   if (printer_paper_layout == vul_psfile::CENTER)
171   {
172     double hsx = box_width / PIX2INCH * scale_x * .5;
173     double hsy = box_height / PIX2INCH * scale_y * .5;
174 
175     // from xv xvps.c subroutine: centerimage
176     pos_inx = psizex * .5 - hsx;
177     pos_iny = psizey * .5 - hsy;
178 
179     // make sure 'center' of image is still on page
180     RANGE(pos_inx, -hsx, psizex - hsx);
181     RANGE(pos_iny, -hsy, psizey - hsy);
182 
183     // round to integer .001ths of an inch
184     pos_inx = std::floor(pos_inx * 1000.0 + 0.5) * .001;
185     pos_iny = std::floor(pos_iny * 1000.0 + 0.5) * .001;
186   }
187   else if (printer_paper_layout == vul_psfile::MAX)
188   {
189     double hsx = psizex - margins[printer_paper_type][0];
190     double hsy = psizey - margins[printer_paper_type][1];
191 
192     // avoid division by 0:
193     if (box_width == 0)
194       box_width = 1;
195     if (box_height == 0)
196       box_height = 1;
197 
198     // choose the smaller scaling factor
199     scale_x = scale_y = (float)std::min(hsx / box_width, hsy / box_height) * PIX2INCH;
200 
201     RANGE(scale_x, ps_minimum, ps_maximum);
202     RANGE(scale_y, ps_minimum, ps_maximum);
203 
204     pos_inx = psizex * .5 - box_width / PIX2INCH * scale_x * .5;
205     pos_iny = psizey * .5 - box_height / PIX2INCH * scale_y * .5;
206 
207     // round to integer .001ths of an inch
208     pos_inx = std::floor(pos_inx * 1000.0 + 0.5) * .001;
209     pos_iny = std::floor(pos_iny * 1000.0 + 0.5) * .001;
210   }
211 
212   // printed image will have size iw,ih (in picas)
213   if (exist_image)
214   {
215     iwf = width * scale_x;
216     iw = int(iwf + 0.5);
217     ihf = height * scale_y;
218     ih = int(ihf + 0.5);
219   }
220   if (exist_objs)
221   {
222     iw = int(box_width * scale_x + 0.5);
223     ih = int(box_height * scale_y + 0.5);
224   }
225 
226   // compute offset to bottom-left of image (in picas)
227   ox = int(pos_inx * PIX2INCH + 0.5);
228   oy = int(pos_iny * PIX2INCH + 0.5);
229 
230   if (debug)
231     std::cout << "vul_psfile::compute_bounding_box, box_width = " << box_width << ", box_height = " << box_height
232               << '\n';
233 }
234 
235 //-----------------------------------------------------------------------------
236 //: Set Bounding Box Min and Max x, y.
237 //-----------------------------------------------------------------------------
238 void
set_min_max_xy(float xx,float yy)239 vul_psfile::set_min_max_xy(float xx, float yy)
240 {
241   int x = int(xx + 0.5);
242   int y = int(yy + 0.5);
243   if (x < min_x)
244     min_x = x;
245   if (y < min_y)
246     min_y = y;
247   if (x > max_x)
248     max_x = x;
249   if (y > max_y)
250     max_y = y;
251 }
252 
253 //-----------------------------------------------------------------------------
254 //: Set Bounding Box Min and Max x, y.
255 //-----------------------------------------------------------------------------
256 void
set_min_max_xy(int x,int y)257 vul_psfile::set_min_max_xy(int x, int y)
258 {
259   if (x < min_x)
260     min_x = x;
261   if (y < min_y)
262     min_y = y;
263   if (x > max_x)
264     max_x = x;
265   if (y > max_y)
266     max_y = y;
267 }
268 
269 //-----------------------------------------------------------------------------
270 //: Write 8 bit grey scale image.
271 //-----------------------------------------------------------------------------
272 void
print_greyscale_image(const unsigned char * buffer,int sizex,int sizey)273 vul_psfile::print_greyscale_image(const unsigned char * buffer, int sizex, int sizey)
274 {
275   if (debug)
276     std::cout << "vul_psfile::print_greyscale_image, width = " << sizex << ", height = " << sizey
277               << ", reduction_factor = " << reduction_factor << '\n';
278 
279   exist_image = true;
280   width = sizex;
281   height = sizey;
282   set_parameters(sizex, sizey);
283   compute_bounding_box();
284 
285   // reduction factor should not be an expansion factor ...
286   if (reduction_factor < 1)
287     reduction_factor = 1;
288 
289   int new_width = (int)std::ceil(sizex / (double)reduction_factor); // round up
290   int new_height = (int)std::ceil(sizey / (double)reduction_factor);
291 
292   output_filestream << "\n%%Page: 1 1\n\n% remember original state\n/origstate save def\n"
293                     << "\n% build a temporary dictionary\n20 dict begin\n\n"
294                     << "% define string to hold a scanline's worth of data\n"
295                     << "/pix " << new_width << " string def\n";
296 
297   if (printer_paper_orientation == vul_psfile::LANDSCAPE)
298     output_filestream << "% print in landscape mode\n90 rotate 0 " << int(-psizey * PIX2INCH) << " translate\n\n";
299   output_filestream << "% lower left corner\n";
300   translate_pos = output_filestream.tellp();
301   image_translate_and_scale();
302 
303   output_filestream << new_width << ' ' << new_height << " 8             % dimensions of data\n"
304                     << '[' << new_width << " 0 0 -" << new_height << " 0 " << new_height
305                     << "]  % mapping matrix\n{currentfile pix readhexstring pop}\nimage\n\n";
306   constexpr int linesize = 72;
307 
308   // write image data to output PostScript file
309   for (int j = 0; j < new_height; j++)
310   {
311     int countrow = 0;
312     for (int i = 0; i < new_width; i++)
313     {
314       int index;
315 
316       if (reduction_factor == 1)
317         index = int(*(buffer + width * j + i));
318       else // Reduce resolution of image if necessary
319       {
320         int pixel_number = (width * j + i) * reduction_factor;
321         index = 0;
322         int number_of_pixels_sampled = 0;
323         for (int m = 0; m < reduction_factor; m++)
324           for (int n = 0; n < reduction_factor; n++)
325             if (i * reduction_factor + m < width && j * reduction_factor + n < height)
326             {
327               index += int(*(buffer + (pixel_number + m + n * width)));
328               ++number_of_pixels_sampled;
329             }
330         if (number_of_pixels_sampled == 0)
331         {
332           std::cerr << "ERROR: Division by 0! " << __FILE__ << __LINE__ << std::endl;
333           throw 0;
334         }
335         index /= number_of_pixels_sampled; // Average the pixel intensity value.
336       }
337 
338       // write hex pixel value
339       if (in_range(index))
340       {
341         char pixel[3];
342         auto low4 = (unsigned char)(index & 0x000f);
343         auto high4 = (unsigned char)((index & 0x00f0) >> 4);
344         pixel[0] = Hex4bit(high4);
345         pixel[1] = Hex4bit(low4);
346         pixel[2] = '\0';
347         output_filestream << pixel;
348       }
349       else
350         std::cout << " index out of range: " << index << '\n';
351 
352       countrow += 2;
353       if (countrow >= linesize)
354       {
355         countrow = 0;
356         output_filestream << '\n';
357       }
358     }
359     output_filestream << '\n';
360   }
361   output_filestream << "% stop using temporary dictionary\nend\n\n"
362                     << "% restore original state\norigstate restore\n\n";
363 }
364 
365 //-----------------------------------------------------------------------------
366 //: Write 24 bit colour image.
367 //-----------------------------------------------------------------------------
368 void
print_color_image(const unsigned char * data,int sizex,int sizey)369 vul_psfile::print_color_image(const unsigned char * data, int sizex, int sizey)
370 {
371   if (debug)
372     std::cout << "vul_psfile::print_color_image, width = " << sizex << ", height = " << sizey
373               << ", reduction_factor = " << reduction_factor << '\n';
374 
375   constexpr int bytes_per_pixel = 3;
376   exist_image = true;
377   width = sizex;
378   height = sizey;
379   set_parameters(sizex, sizey);
380   compute_bounding_box();
381 
382   // reduction factor should not be an expansion factor ...
383   if (reduction_factor < 1)
384     reduction_factor = 1;
385 
386   int new_width = (int)std::ceil(sizex / (double)reduction_factor); // round up
387   int new_height = (int)std::ceil(sizey / (double)reduction_factor);
388 
389   // This part uses xv outfile as a reference:
390   output_filestream << "\n%%Page: 1 1\n\n"
391                     << "% remember original state\n"
392                     << "/origstate save def\n\n"
393                     << "% build a temporary dictionary\n"
394                     << "20 dict begin\n\n"
395                     << "% define string to hold a scanline's worth of data\n"
396                     << "/pix " << 3 * new_width << " string def\n\n"
397                     << "% define space for color conversions\n"
398                     << "/grays " << new_width << " string def  % space for gray scale line\n"
399                     << "/npixls 0 def\n"
400                     << "/rgbindx 0 def\n\n";
401 
402   if (printer_paper_orientation == vul_psfile::LANDSCAPE)
403     output_filestream << "% print in landscape mode\n90 rotate 0 " << int(-psizey * PIX2INCH) << " translate\n\n";
404   output_filestream << "% lower left corner\n";
405   translate_pos = output_filestream.tellp();
406   image_translate_and_scale();
407 
408   output_filestream
409     << "\n% define 'colorimage' if it isn't defined\n"
410     << "% ('colortogray' and 'mergeprocs' come from xwd2ps via xgrab)\n"
411     << "/colorimage where  % do we know about 'colorimage'?\n"
412     << "  { pop }          % yes: pop off the 'dict' returned\n"
413     << "  {                % no:  define one\n"
414     << "    /colortogray { % define an RGB->I function\n"
415     << "      /rgbdata exch store  % call input 'rgbdata'\n"
416     << "      rgbdata length 3 idiv\n"
417     << "      /npixls exch store\n"
418     << "      /rgbindx 0 store\n"
419     << "      0 1 npixls 1 sub {\n"
420     << "        grays exch\n"
421     << "        rgbdata rgbindx       get 20 mul % Red\n"
422     << "        rgbdata rgbindx 1 add get 32 mul % Green\n"
423     << "        rgbdata rgbindx 2 add get 12 mul % Blue\n"
424     << "        add add 64 idiv  % I = .3125 R + .5 G + .1875 B\n"
425     << "        put\n"
426     << "        /rgbindx rgbindx 3 add store\n"
427     << "      } for\n"
428     << "      grays 0 npixls getinterval\n"
429     << "    } bind def\n\n"
430     << "    % Utility procedure for colorimage operator.\n"
431     << "    % This procedure takes two procedures off the stack and merges them into a single procedure.\n\n"
432     << "    /mergeprocs {\n"
433     << "      dup length\n"
434     << "      3 -1 roll\n"
435     << "      dup\n"
436     << "      length\n"
437     << "      dup\n"
438     << "      5 1 roll\n"
439     << "      3 -1 roll\n"
440     << "      add\n"
441     << "      array cvx\n"
442     << "      dup\n"
443     << "      3 -1 roll\n"
444     << "      0 exch\n"
445     << "      putinterval\n"
446     << "      dup\n"
447     << "      4 2 roll\n"
448     << "      putinterval\n"
449     << "    } bind def\n\n"
450     << "    /colorimage {\n"
451     << "      pop pop    % remove 'false 3' operands\n"
452     << "      {colortogray} mergeprocs\n"
453     << "      image\n"
454     << "    } bind def\n"
455     << "  } ifelse    % end of 'false' case\n\n"
456     << new_width << ' ' << new_height << " 8  % dimensions of data\n"
457     << '[' << new_width << " 0 0 -" << new_height << " 0 " << new_height << "]  % mapping matrix\n"
458     << "{currentfile pix readhexstring pop}\n"
459     << "false 3 colorimage\n\n";
460 
461   // write image data into PostScript file.
462   constexpr int linesize = 72;
463 
464   // extract RGB data from pixel value and write it to output file
465   for (int j = 0; j < new_height; j++)
466   {
467     int countrow = 0;
468     for (int i = 0; i < new_width; i++)
469     {
470       for (int c = 0; c < bytes_per_pixel; ++c)
471       {
472         // get RGB hex index.
473         int index;
474 
475         if (reduction_factor == 1)
476           index = int(*(data + (sizex * j + i) * bytes_per_pixel + c));
477         else // Reduce image if necessary
478         {
479           int pixel_number = (sizex * j + i) * bytes_per_pixel * reduction_factor + c;
480           index = 0;
481           int number_of_pixels_sampled = 0;
482           for (int m = 0; m < reduction_factor; m++)
483             for (int n = 0; n < reduction_factor; n++)
484               if (i * reduction_factor + m < sizex && j * reduction_factor + n < sizey)
485               {
486                 index += int(*(data + (pixel_number + (m + n * sizex) * bytes_per_pixel)));
487                 ++number_of_pixels_sampled;
488               }
489           if (number_of_pixels_sampled == 0)
490           {
491             std::cerr << "ERROR: Division by 0! " << __FILE__ << __LINE__ << std::endl;
492             throw 0;
493           }
494           index /= number_of_pixels_sampled; // average the pixel intensity
495         }
496 
497         // write RGC hex.
498         if (in_range(index))
499         {
500           char pixel[3];
501           auto low4 = (unsigned char)(index & 0x000f);
502           auto high4 = (unsigned char)((index & 0x00f0) >> 4);
503           pixel[0] = Hex4bit(high4);
504           pixel[1] = Hex4bit(low4);
505           pixel[2] = '\0';
506           output_filestream << pixel;
507         }
508         else
509           std::cout << " index out of range: " << index << '\n';
510 
511         countrow += 2;
512         if (countrow >= linesize)
513         {
514           countrow = 0;
515           output_filestream << '\n';
516         }
517       }
518     }
519     output_filestream << '\n';
520   }
521 
522   output_filestream << "% stop using temporary dictionary\nend\n\n"
523                     << "% restore original state\norigstate restore\n\n";
524 }
525 
526 //-----------------------------------------------------------------------------
527 //: Set graphic coordinate (translate and rotate to local coordinate).
528 //-----------------------------------------------------------------------------
529 void
graphic_header()530 vul_psfile::graphic_header()
531 {
532   if (printer_paper_orientation == vul_psfile::LANDSCAPE)
533     output_filestream << "% print in landscape mode\n90 rotate 0 " << int(-psizey * PIX2INCH) << " translate\n\n";
534 
535   output_filestream.flush();
536   // save streampos so we can come back and modify it.
537   sobj_t_pos = output_filestream.tellp();
538   // move relative coordinate to local origin and set up scale factor.
539   object_translate_and_scale();
540 }
541 
542 //-----------------------------------------------------------------------------
543 //: Set Image translate and scale.
544 //-----------------------------------------------------------------------------
545 void
image_translate_and_scale()546 vul_psfile::image_translate_and_scale()
547 {
548   int scale_height = int(height * scale_y);
549   int scale_min_x = int(min_x * scale_x);
550   int scale_max_y = int(max_y * scale_y);
551 
552   if (debug)
553     std::cout << "vul_psfile::image_translate_and_scale, scale_height= " << scale_height
554               << ", scale_min_x = " << scale_min_x << ", scale_max_y = " << scale_max_y << '\n';
555 
556   output_filestream << std::setw(6) << ox - scale_min_x << ' ' << std::setw(6) << oy + scale_max_y - scale_height
557                     << " translate\n"
558                     << "\n% size of image (on paper, in 1/72inch coordinates)\n"
559                     << std::setw(9) << iwf << ' ' << std::setw(9) << ihf << " scale\n\n";
560 }
561 
562 //-----------------------------------------------------------------------------
563 //: Set object translate and scale.
564 //-----------------------------------------------------------------------------
565 void
object_translate_and_scale()566 vul_psfile::object_translate_and_scale()
567 {
568   int scale_height = int(box_height * scale_y);
569   int scale_min_x = int(min_x * scale_x);
570   int scale_min_y = int(min_y * scale_y);
571   // round to integer .01ths
572   scale_x = std::floor(scale_x * 100.0f + 0.5f) * .01f;
573   scale_y = std::floor(scale_y * 100.0f + 0.5f) * .01f;
574 
575   // move origin
576   output_filestream << std::setw(6) << ox - scale_min_x << ' ' << std::setw(6) << oy + scale_height + scale_min_y
577                     << " translate\n"
578                     << std::setw(9) << scale_x << ' ' << std::setw(9) << -scale_y << " scale\n\n"
579                     << "/originalCTM matrix currentmatrix def\n";
580 }
581 
582 //-----------------------------------------------------------------------------
583 //: Set ox, oy , iw, ih, iwf, ihf parameters for PostScript file use.
584 //-----------------------------------------------------------------------------
585 bool
set_parameters(int sizex,int sizey)586 vul_psfile::set_parameters(int sizex, int sizey)
587 {
588   width = sizex;
589   height = sizey;
590   // avoid division by 0 or other fancy things later on:
591   assert(width > 0 && height > 0);
592 
593   set_min_max_xy(0, 0);
594   set_min_max_xy(width, height);
595   compute_bounding_box();
596 
597   return true;
598 }
599 
600 //-----------------------------------------------------------------------------
601 //: PostScript file header.
602 //-----------------------------------------------------------------------------
603 void
postscript_header()604 vul_psfile::postscript_header()
605 {
606   if (header_pos != HEADER_START)
607   {
608     std::cerr << "vul_psfile: Header already set to " << long(header_pos) << '\n';
609     return;
610   }
611 
612   output_filestream << "%!PS-Adobe-2.0 EPSF-2.0\n%%Title: " << filename.c_str()
613                     << "\n%%Creator: vul_psfile\n%%BoundingBox: ";
614 
615   header_pos = output_filestream.tellp();
616   reset_postscript_header();
617 }
618 
619 
620 //-----------------------------------------------------------------------------
621 //: Reset PostScript header file
622 //-----------------------------------------------------------------------------
623 void
reset_postscript_header()624 vul_psfile::reset_postscript_header()
625 {
626   if (printer_paper_orientation == vul_psfile::LANDSCAPE)
627     output_filestream << std::setw(6) << int(pos_iny * PIX2INCH + 0.5) << ' ' << std::setw(6)
628                       << int(pos_inx * PIX2INCH + 0.5) << ' ' << std::setw(6) << int(pos_iny * PIX2INCH + 0.5) + ih
629                       << ' ' << std::setw(6) << int(pos_inx * PIX2INCH + 0.5) + iw << '\n';
630   else
631     output_filestream << std::setw(6) << ox << ' ' << std::setw(6) << oy << ' ' << std::setw(6) << ox + iw << ' '
632                       << std::setw(6) << oy + ih << '\n';
633   output_filestream << "%%Pages: 1\n%%DocumentFonts:\n%%EndComments\n";
634 }
635 
636 //-----------------------------------------------------------------------------
637 //: Utility program used in point(), line(), ellipse() and circle()
638 //-----------------------------------------------------------------------------
639 void
sobj_rgb_params(char const * obj_str,bool filled)640 vul_psfile::sobj_rgb_params(char const * obj_str, bool filled)
641 {
642   print_graphics_prolog();
643   output_filestream << "\nBegin %I " << obj_str << "\n2 0 0 [] 0 SetB\n"
644                     << fg_r << ' ' << fg_g << ' ' << fg_b << " SetCFg\n"
645                     << bg_r << ' ' << bg_g << ' ' << bg_b << " SetCBg\n"
646                     << line_width_ << " setlinewidth\n"
647                     << (filled ? "0" : "none") << " SetP %I p n\n";
648 }
649 
650 //-----------------------------------------------------------------------------
651 //:  Add a line between the given points to the Postscript file.
652 //-----------------------------------------------------------------------------
653 void
line(float x1,float y1,float x2,float y2)654 vul_psfile::line(float x1, float y1, float x2, float y2)
655 {
656   // set up bounding box.
657   set_min_max_xy(x1, y1);
658   set_min_max_xy(x2, y2);
659   compute_bounding_box();
660 
661   print_graphics_prolog();
662   sobj_rgb_params("Line", false);
663 
664   output_filestream << int(x1) << ' ' << int(y1) << ' ' << int(x2) << ' ' << int(y2) << " Line\nEnd\n";
665 }
666 
667 //-----------------------------------------------------------------------------
668 //: Add a point at the given coordinates to the Postscript file.
669 //-----------------------------------------------------------------------------
670 void
point(float x,float y,float point_size)671 vul_psfile::point(float x, float y, float point_size)
672 {
673   print_graphics_prolog();
674   set_min_max_xy(x, y);
675   compute_bounding_box();
676 
677   this->sobj_rgb_params("Point", true);
678 
679   point_size /= 2;
680   output_filestream << x << ' ' << y << ' ' << point_size << ' ' << point_size << " Elli\nEnd\n";
681 }
682 
683 //-----------------------------------------------------------------------------
684 //: Add an ellipse to the Postscript file.
685 //-----------------------------------------------------------------------------
686 void
ellipse(float x,float y,float a_axis,float b_axis,int angle)687 vul_psfile::ellipse(float x, float y, float a_axis, float b_axis, int angle)
688 {
689 #ifndef PI // should already be defined in math.h - PVR
690 #  define PI 3.14159265358979323846
691 #endif
692   const double radsperdeg = PI / 180.0;
693 
694   set_min_max_xy(int(x + a_axis * std::cos(angle * radsperdeg) + 0.5),
695                  int(y + a_axis * std::sin(angle * radsperdeg) + 0.5));
696   set_min_max_xy(int(x - a_axis * std::cos(angle * radsperdeg) + 0.5),
697                  int(y - a_axis * std::sin(angle * radsperdeg) + 0.5));
698   compute_bounding_box();
699 
700   print_graphics_prolog();
701   sobj_rgb_params("Ellipse", false);
702   if (angle)
703     output_filestream << (int)x << ' ' << (int)y << " translate\n"
704                       << -angle << " rotate\n0 0 " << (int)a_axis << ' ' << (int)b_axis << " Elli\nEnd\n";
705   else
706     output_filestream << (int)x << ' ' << (int)y << ' ' << (int)a_axis << ' ' << (int)b_axis << " Elli\nEnd\n";
707 }
708 
709 //-----------------------------------------------------------------------------
710 //: Add a circle with the given centre point and radius to the Postscript file.
711 //-----------------------------------------------------------------------------
712 void
circle(float x,float y,float radius)713 vul_psfile::circle(float x, float y, float radius)
714 {
715   // set up bounding box
716   set_min_max_xy(x + radius, y);
717   set_min_max_xy(x - radius, y);
718   set_min_max_xy(x, y + radius);
719   set_min_max_xy(x, y - radius);
720   compute_bounding_box();
721 
722   print_graphics_prolog();
723   sobj_rgb_params("Circle", false);
724   ellipse(x, y, radius, radius);
725   output_filestream << "End\n";
726 }
727 
728 //-----------------------------------------------------------------------------
729 //: the defined procedure for PostScript script use.
730 //-----------------------------------------------------------------------------
731 void
print_graphics_prolog()732 vul_psfile::print_graphics_prolog()
733 {
734   if (graphics_prolog_exists)
735     return;
736   exist_objs = true;
737   output_filestream
738     << "\n\n%%BeginTargetjrPrologue\n"
739     << "/arrowhead {\n"
740     << "  0 begin\n"
741     << "  transform originalCTM itransform\n"
742     << "  /taily exch def\n"
743     << "  /tailx exch def\n"
744     << "  transform originalCTM itransform\n"
745     << "  /tipy exch def\n"
746     << "  /tipx exch def\n"
747     << "  /dy tipy taily sub def\n"
748     << "  /dx tipx tailx sub def\n"
749     << "  /angle dx 0 ne dy 0 ne or { dy dx atan } { 90 } ifelse def\n"
750     << "  gsave\n"
751     << "  originalCTM setmatrix\n"
752     << "  tipx tipy translate\n"
753     << "  angle rotate\n"
754     << "  newpath\n"
755     << "  arrowHeight neg arrowWidth 2 div moveto\n"
756     << "  0 0 lineto\n"
757     << "  arrowHeight neg arrowWidth 2 div neg lineto\n"
758     << "  patternNone not {\n"
759     << "    originalCTM setmatrix\n"
760     << "    /padtip arrowHeight 2 exp 0.25 arrowWidth 2 exp mul add sqrt brushWidth mul\n"
761     << "    arrowWidth div def\n"
762     << "    /padtail brushWidth 2 div def\n"
763     << "    tipx tipy translate\n"
764     << "    angle rotate\n"
765     << "    padtip 0 translate\n"
766     << "    arrowHeight padtip add padtail add arrowHeight div dup scale\n"
767     << "    arrowheadpath\n"
768     << "    ifill\n"
769     << "  } if\n"
770     << "  brushNone not {\n"
771     << "    originalCTM setmatrix\n"
772     << "    tipx tipy translate\n"
773     << "    angle rotate\n"
774     << "    arrowheadpath\n"
775     << "    istroke\n"
776     << "  } if\n"
777     << "  grestore\n"
778     << "  end\n"
779     << "} dup 0 9 dict put def\n\n"
780     << "/arrowheadpath {\n"
781     << "  newpath\n"
782     << "  arrowHeight neg arrowWidth 2 div moveto\n"
783     << "  0 0 lineto\n"
784     << "  arrowHeight neg arrowWidth 2 div neg lineto\n"
785     << "} def\n\n"
786     << "/leftarrow {\n"
787     << "  0 begin\n"
788     << "  y exch get /taily exch def\n"
789     << "  x exch get /tailx exch def\n"
790     << "  y exch get /tipy exch def\n"
791     << "  x exch get /tipx exch def\n"
792     << "  brushLeftArrow { tipx tipy tailx taily arrowhead } if\n"
793     << "  end\n"
794     << "} dup 0 4 dict put def\n\n"
795     << "/rightarrow {\n"
796     << "  0 begin\n"
797     << "  y exch get /tipy exch def\n"
798     << "  x exch get /tipx exch def\n"
799     << "  y exch get /taily exch def\n"
800     << "  x exch get /tailx exch def\n"
801     << "  brushRightArrow { tipx tipy tailx taily arrowhead } if\n"
802     << "  end\n"
803     << "} dup 0 4 dict put def\n\n"
804     << "%%EndTargetjrPrologue\n\n"
805     << "/arrowHeight 10 def\n"
806     << "/arrowWidth 5 def\n\n"
807     << "/TargetjrDict 50 dict def\n"
808     << "TargetjrDict begin\n\n"
809     << "/none null def\n"
810     << "/numGraphicParameters 17 def\n"
811     << "/stringLimit 65535 def\n\n"
812     << "/Begin { save numGraphicParameters dict begin } def\n\n"
813     << "/End { end restore } def\n\n"
814     << "/SetB { % width leftarrow rightarrow DashArray DashOffset SetB\n"
815     << "  dup type /nulltype eq {\n"
816     << "    pop\n"
817     << "    false /brushRightArrow idef\n"
818     << "    false /brushLeftArrow idef\n"
819     << "    true /brushNone idef\n"
820     << "  } {\n"
821     << "    /brushDashOffset idef\n"
822     << "    /brushDashArray idef\n"
823     << "    0 ne /brushRightArrow idef\n"
824     << "    0 ne /brushLeftArrow idef\n"
825     << "    /brushWidth idef\n"
826     << "    false /brushNone idef\n"
827     << "  } ifelse\n"
828     << "} def\n\n"
829     << "/SetCFg { /fgblue idef /fggreen idef /fgred idef } def\n\n"
830     << "/SetCBg { /bgblue idef /bggreen idef /bgred idef } def\n\n"
831     << "/SetF { /printSize idef /printFont idef } def\n\n"
832     << "/SetP {  % string -1 SetP  OR gray SetP\n"
833     << "  dup type /nulltype eq { pop true /patternNone idef }\n"
834     << "  {\n"
835     << "    dup -1 eq { /patternGrayLevel idef /patternString idef }\n"
836     << "    { /patternGrayLevel idef } ifelse\n"
837     << "    false /patternNone idef\n"
838     << "  } ifelse\n"
839     << "} def\n\n"
840     << "/BSpl {\n"
841     << "  0 begin\n"
842     << "  storexyn\n"
843     << "  newpath\n"
844     << "  n 1 gt {\n"
845     << "    0 0 0 0 0 0 1 1 true subspline\n"
846     << "    n 2 gt {\n"
847     << "      0 0 0 0 1 1 2 2 false subspline\n"
848     << "      1 1 n 3 sub { /i exch def i 1 sub dup i dup i 1 add dup i 2 add dup false subspline } for\n"
849     << "      n 3 sub dup n 2 sub dup n 1 sub dup 2 copy false subspline\n"
850     << "    } if\n"
851     << "    n 2 sub dup n 1 sub dup 2 copy 2 copy false subspline\n"
852     << "    patternNone not brushLeftArrow not brushRightArrow not and and { ifill } if\n"
853     << "    brushNone not { istroke } if\n"
854     << "    0 0 1 1 leftarrow\n"
855     << "    n 2 sub dup n 1 sub dup rightarrow\n"
856     << "  } if\n"
857     << "  end\n"
858     << "} dup 0 4 dict put def\n\n"
859     << "/Circ { newpath 0 360 arc patternNone not { ifill } if brushNone not { istroke } if } def\n\n"
860     << "/CBSpl {\n"
861     << "  0 begin\n"
862     << "  dup 2 gt {\n"
863     << "    storexyn\n"
864     << "    newpath\n"
865     << "    n 1 sub dup 0 0 1 1 2 2 true subspline\n"
866     << "    1 1 n 3 sub { /i exch def i 1 sub dup i dup i 1 add dup i 2 add dup false subspline } for\n"
867     << "    n 3 sub dup n 2 sub dup n 1 sub dup 0 0 false subspline\n"
868     << "    n 2 sub dup n 1 sub dup 0 0 1 1 false subspline\n"
869     << "    patternNone not { ifill } if\n"
870     << "    brushNone not { istroke } if\n"
871     << "  } { Poly } ifelse\n"
872     << "  end\n"
873     << "} dup 0 4 dict put def\n"
874     << "/Elli {\n"
875     << "  0 begin\n"
876     << "  newpath 4 2 roll translate scale\n"
877     << "  0 0 1 0 360 arc\n"
878     << "  patternNone not { ifill } if\n"
879     << "  brushNone not { istroke } if\n"
880     << "  end\n"
881     << "} dup 0 1 dict put def\n\n"
882     << "/Line {\n"
883     << "  0 begin\n"
884     << "  2 storexyn\n"
885     << "  newpath\n"
886     << "  x 0 get y 0 get moveto\n"
887     << "  x 1 get y 1 get lineto\n"
888     << "  brushNone not { istroke } if\n"
889     << "  0 0 1 1 leftarrow\n"
890     << "  0 0 1 1 rightarrow\n"
891     << "  end\n"
892     << "} dup 0 4 dict put def\n\n"
893     << "/MLine {\n"
894     << "  0 begin\n"
895     << "  storexyn\n"
896     << "  newpath\n"
897     << "  n 1 gt {\n"
898     << "    x 0 get y 0 get moveto\n"
899     << "    1 1 n 1 sub { /i exch def x i get y i get lineto } for\n"
900     << "    patternNone not brushLeftArrow not brushRightArrow not and and { ifill } if\n"
901     << "    brushNone not { istroke } if\n"
902     << "    0 0 1 1 leftarrow\n"
903     << "    n 2 sub dup n 1 sub dup rightarrow\n"
904     << "  } if\n"
905     << "  end\n"
906     << "} dup 0 4 dict put def\n\n"
907     << "/Poly {\n"
908     << "  3 1 roll\n"
909     << "  newpath moveto -1 add { lineto } repeat closepath\n"
910     << "  patternNone not { ifill } if\n"
911     << "  brushNone not { istroke } if\n"
912     << "} def\n\n"
913     << "/Rect {\n"
914     << "  0 begin\n"
915     << "  /t exch def\n"
916     << "  /r exch def\n"
917     << "  /b exch def\n"
918     << "  /l exch def\n"
919     << "  newpath\n"
920     << "  l b moveto\n"
921     << "  l t lineto\n"
922     << "  r t lineto\n"
923     << "  r b lineto\n"
924     << "  closepath\n"
925     << "  patternNone not { ifill } if\n"
926     << "  brushNone not { istroke } if\n"
927     << "  end\n"
928     << "} dup 0 4 dict put def\n\n"
929     << "/Text { ishow } def\n\n"
930     << "/idef { dup where { pop pop pop } { exch def } ifelse } def\n\n"
931     << "/ifill {\n"
932     << "  0 begin\n"
933     << "  gsave\n"
934     << "  patternGrayLevel -1 ne {\n"
935     << "    fgred bgred fgred sub patternGrayLevel mul add\n"
936     << "    fggreen bggreen fggreen sub patternGrayLevel mul add\n"
937     << "    fgblue bgblue fgblue sub patternGrayLevel mul add setrgbcolor\n"
938     << "    eofill\n"
939     << "  } {\n"
940     << "    eoclip\n"
941     << "    originalCTM setmatrix\n"
942     << "    pathbbox /t exch def /r exch def /b exch def /l exch def\n"
943     << "    /w r l sub ceiling cvi def\n"
944     << "    /h t b sub ceiling cvi def\n"
945     << "    /imageByteWidth w 8 div ceiling cvi def\n"
946     << "    /imageHeight h def\n"
947     << "    bgred bggreen bgblue setrgbcolor\n"
948     << "    eofill\n"
949     << "    fgred fggreen fgblue setrgbcolor\n"
950     << "    w 0 gt h 0 gt and { l w add b translate w neg h scale w h true [w 0 0 h neg 0 h] { patternproc } imagemask "
951        "} if\n"
952     << "  } ifelse\n"
953     << "  grestore\n"
954     << "  end\n"
955     << "} dup 0 8 dict put def\n\n"
956     << "/istroke {\n"
957     << "  gsave\n"
958     << "  brushDashOffset -1 eq { [] 0 setdash 1 setgray }\n"
959     << "  { brushDashArray brushDashOffset setdash fgred fggreen fgblue setrgbcolor } ifelse\n"
960     << "  originalCTM setmatrix\n"
961     << "  stroke\n"
962     << "  grestore\n"
963     << "} def\n\n"
964     << "/ishow {\n"
965     << "  0 begin\n"
966     << "  gsave\n"
967     << "  fgred fggreen fgblue setrgbcolor\n"
968     << "  /fontDict printFont printSize scalefont dup setfont def\n"
969     << "  /descender fontDict begin 0 [FontBBox] 1 get FontMatrix end\n"
970     << "  transform exch pop def\n"
971     << "  /vertoffset 1 printSize sub descender sub def\n"
972     << "  { 0 vertoffset moveto show /vertoffset vertoffset printSize sub def } forall\n"
973     << "  grestore\n"
974     << "  end\n"
975     << "} dup 0 3 dict put def\n"
976     << "/patternproc {\n"
977     << "  0 begin\n"
978     << "  /patternByteLength patternString length def\n"
979     << "  /patternHeight patternByteLength 8 mul sqrt cvi def\n"
980     << "  /patternWidth patternHeight def\n"
981     << "  /patternByteWidth patternWidth 8 idiv def\n"
982     << "  /imageByteMaxLength imageByteWidth imageHeight mul\n"
983     << "  stringLimit patternByteWidth sub min def\n"
984     << "  /imageMaxHeight imageByteMaxLength imageByteWidth idiv patternHeight idiv\n"
985     << "  patternHeight mul patternHeight max def\n"
986     << "  /imageHeight imageHeight imageMaxHeight sub store\n"
987     << "  /imageString imageByteWidth imageMaxHeight mul patternByteWidth add string def\n"
988     << "  0 1 imageMaxHeight 1 sub {\n"
989     << "    /y exch def\n"
990     << "    /patternRow y patternByteWidth mul patternByteLength mod def\n"
991     << "    /patternRowString patternString patternRow patternByteWidth getinterval def\n"
992     << "    /imageRow y imageByteWidth mul def\n"
993     << "    0 patternByteWidth imageByteWidth 1 sub { /x exch def imageString imageRow x add patternRowString "
994        "putinterval } for\n"
995     << "  } for\n"
996     << "  imageString\n"
997     << "  end\n"
998     << "} dup 0 12 dict put def\n\n"
999     << "/min { dup 3 2 roll dup 4 3 roll lt { exch } if pop } def\n\n"
1000     << "/max { dup 3 2 roll dup 4 3 roll gt { exch } if pop } def\n\n"
1001     << "/midpoint {\n"
1002     << "  0 begin\n"
1003     << "  /y1 exch def\n"
1004     << "  /x1 exch def\n"
1005     << "  /y0 exch def\n"
1006     << "  /x0 exch def\n"
1007     << "  x0 x1 add 2 div\n"
1008     << "  y0 y1 add 2 div\n"
1009     << "  end\n"
1010     << "} dup 0 4 dict put def\n\n"
1011     << "/thirdpoint {\n"
1012     << "  0 begin\n"
1013     << "  /y1 exch def\n"
1014     << "  /x1 exch def\n"
1015     << "  /y0 exch def\n"
1016     << "  /x0 exch def\n"
1017     << "  x0 2 mul x1 add 3 div\n"
1018     << "  y0 2 mul y1 add 3 div\n"
1019     << "  end\n"
1020     << "} dup 0 4 dict put def\n\n"
1021     << "/subspline {\n"
1022     << "  0 begin\n"
1023     << "  /movetoNeeded exch def\n"
1024     << "  y exch get /y3 exch def\n"
1025     << "  x exch get /x3 exch def\n"
1026     << "  y exch get /y2 exch def\n"
1027     << "  x exch get /x2 exch def\n"
1028     << "  y exch get /y1 exch def\n"
1029     << "  x exch get /x1 exch def\n"
1030     << "  y exch get /y0 exch def\n"
1031     << "  x exch get /x0 exch def\n"
1032     << "  x1 y1 x2 y2 thirdpoint\n"
1033     << "  /p1y exch def\n"
1034     << "  /p1x exch def\n"
1035     << "  x2 y2 x1 y1 thirdpoint\n"
1036     << "  /p2y exch def\n"
1037     << "  /p2x exch def\n"
1038     << "  x1 y1 x0 y0 thirdpoint\n"
1039     << "  p1x p1y midpoint\n"
1040     << "  /p0y exch def\n"
1041     << "  /p0x exch def\n"
1042     << "  x2 y2 x3 y3 thirdpoint\n"
1043     << "  p2x p2y midpoint\n"
1044     << "  /p3y exch def\n"
1045     << "  /p3x exch def\n"
1046     << "  movetoNeeded { p0x p0y moveto } if\n"
1047     << "  p1x p1y p2x p2y p3x p3y curveto\n"
1048     << "  end\n"
1049     << "} dup 0 17 dict put def\n\n"
1050     << "/storexyn {\n"
1051     << "  /n exch def\n"
1052     << "  /y n array def\n"
1053     << "  /x n array def\n"
1054     << "  n 1 sub -1 0 { /i exch def y i 3 2 roll put x i 3 2 roll put } for\n"
1055     << "} def\n\n"
1056     << "/SSten { fgred fggreen fgblue setrgbcolor dup true exch 1 0 0 -1 0 6 -1 roll matrix astore } def\n\n"
1057     << "/FSten {\n"
1058     << "  dup 3 -1 roll dup 4 1 roll exch\n"
1059     << "  newpath\n"
1060     << "  0 0 moveto\n"
1061     << "  dup 0 exch lineto\n"
1062     << "  exch dup 3 1 roll exch lineto\n"
1063     << "  0 lineto\n"
1064     << "  closepath\n"
1065     << "  bgred bggreen bgblue setrgbcolor\n"
1066     << "  eofill\n"
1067     << "  SSten\n"
1068     << "} def\n\n"
1069     << "/Rast { exch dup 3 1 roll 1 0 0 -1 0 6 -1 roll matrix astore } def\n\n";
1070 
1071   // For scale and translate ..
1072   graphic_header();
1073 
1074   graphics_prolog_exists = true;
1075 }
1076 
1077 void
done()1078 vul_psfile::done()
1079 {
1080   if (debug)
1081     std::cout << "vul_psfile::done\n";
1082   doneps = true;
1083   if (graphics_prolog_exists)
1084     output_filestream << "end % TargetjrDict\n";
1085 
1086   output_filestream << "showpage\n%%Trailer\n";
1087 }
1088