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