1 
2 /******************************************************************************
3 * MODULE     : printer.cpp
4 * DESCRIPTION: Renderer for printing post-script graphics
5 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "printer.hpp"
13 #include "Metafont/tex_files.hpp"
14 #include "Freetype/tt_file.hpp"
15 #include "file.hpp"
16 #include "image_files.hpp"
17 #include "analyze.hpp"
18 #include "iterator.hpp"
19 #include "merge_sort.hpp"
20 #include "scheme.hpp"
21 #include "image_files.hpp"
22 #include "link.hpp"
23 #include "frame.hpp"
24 #include "converter.hpp"
25 
26 string PS_CLIP_PUSH ("gsave");
27 string PS_CLIP_POP ("grestore");
28 string PS_CLIP ("cl");
29 string PS_LINE ("ln");
30 string PS_FILL ("fl");
31 string PS_ARC ("ac");
32 string PS_FILL_ARC ("fac");
33 string PS_STROKE ("st");
34 string PS_POL_START ("sp");
35 string PS_POL_NEXT ("np");
36 string PS_POL_END ("ep");
37 string PS1 ("u");
38 string PS2 ("z");
39 
40 /******************************************************************************
41 * constructors and destructors
42 ******************************************************************************/
43 
printer_rep(url ps_file_name2,int dpi2,int nr_pages2,string page_type2,bool landscape2,double paper_w2,double paper_h2)44 printer_rep::printer_rep (
45   url ps_file_name2, int dpi2, int nr_pages2,
46   string page_type2, bool landscape2, double paper_w2, double paper_h2):
47     renderer_rep (false),
48     ps_file_name (ps_file_name2), dpi (dpi2),
49     nr_pages (nr_pages2), page_type (page_type2),
50     landscape (landscape2), paper_w (paper_w2), paper_h (paper_h2),
51     use_alpha (get_preference ("experimental alpha") == "on"),
52     linelen (0), fg ((color) (-1)), bg ((color) (-1)), opacity (255),
53     ncols (0), lw (-1), nwidths (0), cfn (""), nfonts (0),
54     xpos (0), ypos (0), tex_flag (false), toc (TUPLE),
55     defs ("?"), tex_chars ("?"), tex_width ("?"),
56     tex_fonts ("?"), tex_font_chars (array<int>(0)), metadata ("")
57 {
58   string tex_pro, special_pro, color_pro, texps_pro;
59   load_string ("$TEXMACS_PATH/misc/convert/tex.pro", tex_pro, true);
60   load_string ("$TEXMACS_PATH/misc/convert/special.pro", special_pro, true);
61   load_string ("$TEXMACS_PATH/misc/convert/color.pro", color_pro, true);
62   load_string ("$TEXMACS_PATH/misc/convert/texps.pro", texps_pro, true);
63 
64   prologue   << "%!PS-Adobe-2.0";
65   if (suffix (ps_file_name) == "eps")
66     prologue << " EPSF-2.0";
67   prologue   << "\n"
68 	     << "%%Creator: TeXmacs-" TEXMACS_VERSION "\n"
69 	     << "%%Title: " << as_string (tail (ps_file_name)) << "\n"
70 	     << "%%Pages: " << as_string (nr_pages) << "\n"
71 	     << "%%PageOrder: Ascend\n";
72   if (page_type != "user")
73     prologue << "%%DocumentPaperSizes: " << page_type << "\n";
74   if (landscape) {
75     psw= (int) (28.36*paper_h+ 0.5);
76     psh= (int) (28.36*paper_w+ 0.5);
77   }
78   else {
79     psw= (int) (28.36*paper_w+ 0.5);
80     psh= (int) (28.36*paper_h+ 0.5);
81   }
82   prologue << "%%BoundingBox: 0 0 "
83            << as_string (psw) << " "
84            << as_string (psh) << "\n";
85   if (landscape)
86     prologue << "%%Orientation: Landscape\n";
87   prologue   << "%%EndComments\n\n"
88 	     << tex_pro << "\n"
89 	     << special_pro << "\n"
90 	     << texps_pro << "\n"
91 	     << "TeXDict begin\n"
92 	     << as_string ((int) (1864680.0*paper_w+ 0.5)) << " "
93 	     << as_string ((int) (1864680.0*paper_h+ 0.5)) << " 1000 "
94 	     << as_string (dpi) << " " << as_string (dpi)
95 	     << " (TeXmacs) @start\n";
96 
97   define (PS_CLIP, string ("/pt4 X /pt3 X /pt2 X /pt1 X\n") *
98 	  string ("newpath pt1 pt2 moveto pt3 pt2 lineto ") *
99 	  string ("pt3 pt4 lineto pt1 pt4 lineto pt1 pt2 lineto clip"));
100   define (PS_LINE, string ("/pt4 X /pt3 X /pt2 X /pt1 X\n") *
101 	  string ("newpath pt1 pt2 moveto pt3 pt4 lineto stroke"));
102   define (PS_FILL, string ("/pt4 X /pt3 X /pt2 X /pt1 X\n") *
103 	  string ("newpath pt1 pt2 moveto pt3 pt2 lineto ") *
104 	  string ("pt3 pt4 lineto pt1 pt4 lineto pt1 pt2 eofill stroke"));
105   define (PS_ARC, string ("/a2 X /a1 X /r2 X /r1 X /pt2 X /pt1 X\n") *
106 	  string ("newpath pt1 pt2 r1 r2 a1 a2 ellipse stroke"));
107   define (PS_FILL_ARC, string ("/a2 X /a1 X /r2 X /r1 X /pt2 X /pt1 X\n") *
108 	  string ("newpath pt1 pt2 r1 r2 a1 a2 ellipse eofill stroke"));
109   define (PS_STROKE, string ("stroke"));
110   define (PS_POL_START, string ("/pt2 X /pt1 X\n") *
111 	  string ("newpath pt1 pt2 moveto"));
112   define (PS_POL_NEXT, string ("/pt2 X /pt1 X\n") *
113 	  string ("pt1 pt2 lineto"));
114   define (PS_POL_END, string ("closepath eofill"));
115   define (PS1, string ("gsave"));
116   define (PS2, string ("1 -1 scale show grestore"));
117 
118   cur_page= 0;
119   next_page ();
120 }
121 
~printer_rep()122 printer_rep::~printer_rep () {
123   next_page ();
124   generate_toc ();
125   generate_metadata ();
126   body << "\n%%Trailer\n"
127        << "end\n"
128        << "userdict /end-hook known{end-hook} if\n"
129        << "%%EOF\n";
130 
131   generate_tex_fonts ();
132   prologue << "end\n"
133 
134            << "systemdict /pdfmark known{userdict /?pdfmark systemdict /exec get put}{userdict /?pdfmark systemdict /pop get put userdict /pdfmark systemdict /cleartomark get put}ifelse\n"
135 
136            << "%%EndProlog\n\n"
137 	   << "%%BeginSetup\n"
138 	   << "%%Feature: *Resolution " << as_string (dpi) << "dpi\n"
139 	   << "TeXDict begin\n";
140   prologue << "%%BeginPaperSize: " << page_type << "\n";
141   if (page_type != "user")
142     prologue << page_type << "\n";
143   else {
144     prologue << "/setpagedevice where\n";
145     prologue << "{ pop << /PageSize ["
146              << as_string (psw) << " " << as_string (psh)
147              << "] >> setpagedevice }\n";
148     prologue << "if\n";
149   }
150   prologue << "%%EndPaperSize\n";
151   if (landscape)
152     prologue << "@landscape\n";
153   prologue << "%%EndSetup\n";
154 
155   string ps_text= prologue * "\n" * body;
156   save_string (ps_file_name, ps_text);
157 }
158 
159 bool
is_printer()160 printer_rep::is_printer () {
161   return true;
162 }
163 
164 void
next_page()165 printer_rep::next_page () {
166   if (cur_page > 0) print ("eop\n");
167   if (cur_page >= nr_pages) return;
168   cur_page++;
169   body << "\n%%Page: " << as_string (cur_page) << " "
170        << as_string (cur_page) << "\n"
171        << as_string (cur_page) << " "
172        << as_string (cur_page-1) << " bop\n";
173 
174   set_clipping (0, (int) (-(dpi*PIXEL*paper_h)/2.54),
175 		(int) ((dpi*PIXEL*paper_w)/2.54), 0);
176 
177   fg  = (color) (-1);
178   bg  = (color) (-1);
179   lw  = -1;
180   cfn = "";
181   xpos= 0;
182   ypos= 0;
183 }
184 
185 /******************************************************************************
186 * subroutines for printing
187 ******************************************************************************/
188 
189 void
define(string s,string defn)190 printer_rep::define (string s, string defn) {
191   if (defs->contains (s)) return;
192   defs (defn)= s;
193   prologue << "/" << s << " {" << defn << "} N\n";
194 }
195 
196 void
sep()197 printer_rep::sep () {
198   if ((N(body) > 0) &&
199       (body [N(body)-1] != ')') &&
200       (body [N(body)-1] != '\n')) {
201     body << " ";
202     linelen++;
203     tex_flag= false;
204   }
205 }
206 
207 void
cr()208 printer_rep::cr () {
209   body << "\n";
210   linelen= 0;
211   tex_flag= false;
212 }
213 
214 void
print(string s)215 printer_rep::print (string s) {
216   if (N(s)==0) return;
217   if ((linelen>0) && (linelen+N(s)>79)) {
218     body << "\n";
219     linelen= 0;
220     tex_flag= false;
221   }
222   else if (s[0]!='(') sep ();
223   if (tex_flag && (s[0]=='(')) {
224     body->resize (N(body)-2);
225     linelen -= 2;
226     s= s (1,N(s));
227   }
228   body << s;
229   linelen += N(s);
230   tex_flag= false;
231 }
232 
233 void
print(SI x,SI y)234 printer_rep::print (SI x, SI y) {
235   decode (x, y);
236   print (as_string (x-dpi));
237   print (as_string (y-dpi));
238 }
239 
240 void
move_to(SI x,SI y)241 printer_rep::move_to (SI x, SI y) {
242   x += ox; y += oy;
243   if (x>=0) x= x/PIXEL; else x= (x-PIXEL+1)/PIXEL;
244   if (y>=0) y= y/PIXEL; else y= (y-PIXEL+1)/PIXEL;
245   if (tex_flag && (xpos==x) && (ypos==y)) return;
246   if (tex_flag && (ypos==y)) {
247     body->resize (N(body)-1);
248     linelen -= 1;
249     tex_flag= false;
250 
251     int diff= x-xpos;
252     if ((diff>=-4) && (diff<=4)) print (string ((char) ('p'+diff)));
253     else {
254       print (as_string (diff));
255       print ("b");
256     }
257     xpos= x;
258     return;
259   }
260   xpos= x; ypos= y;
261   print (as_string (x-dpi));
262   print (as_string (-y-dpi));
263   print ("a");
264 }
265 
266 string
define_alpha(int a)267 printer_rep::define_alpha (int a) {
268   string aa= as_string (((double) a) / 255.0);
269   string s= "[ /ca " * aa * " /CA " * aa * " /SetTransparency pdfmark";
270   if (!defs->contains (s)) define ("A" * as_string (a), s);
271   return defs[s];
272 }
273 
274 void
select_color(color c)275 printer_rep::select_color (color c) {
276   int r, g, b, a;
277   get_rgb_color (c, r, g, b, a);
278   r= 10000+ ((r*1000)/255);
279   g= 10000+ ((g*1000)/255);
280   b= 10000+ ((b*1000)/255);
281   string rr= as_string (r); rr= rr(1,2) * "." * rr(2,5);
282   string gg= as_string (g); gg= gg(1,2) * "." * gg(2,5);
283   string bb= as_string (b); bb= bb(1,2) * "." * bb(2,5);
284   string s = rr * " " * gg * " " * bb * " setrgbcolor";
285   if (use_alpha) s= s * " " * define_alpha (a);
286   if (!defs->contains (s)) {
287     define ("C" * as_string (ncols), s);
288     ncols++;
289   }
290   print (defs[s]);
291 }
292 
293 void
select_line_width(SI w)294 printer_rep::select_line_width (SI w) {
295   w= w/PIXEL; if (w<1) w=1;
296   string s = as_string (w) * " setlinewidth";
297   if (!defs->contains (s)) {
298     define ("W" * as_string (nwidths), s);
299     nwidths++;
300   }
301   print (defs[s]);
302 }
303 
304 /******************************************************************************
305 * subroutines for fonts
306 ******************************************************************************/
307 
308 static string
prepare_text(string s)309 prepare_text (string s) {
310   int i;
311   string r;
312   for (i=0; i<N(s); i++) {
313     int c= ((unsigned char) s[i]);
314     if ((s[i]=='(') || (s[i]==')') || (s[i]=='\\'))
315       r << '\\' << s[i];
316     else if ((c <= 32) || (c >= 128)) {
317       r << '\\';
318       r << ('0' + (c >> 6));
319       r << ('0' + ((c >> 3) & 7));
320       r << ('0' + (c & 7));
321     }
322     else r << s[i];
323   }
324   return r;
325 }
326 
327 void
select_tex_font(string name)328 printer_rep::select_tex_font (string name) {
329   if (cfn==name) return;
330   cfn= name;
331   print (tex_fonts [name]);
332 }
333 
334 /******************************************************************************
335 * make tex characters and fonts
336 ******************************************************************************/
337 
338 static const char* hex_string= "0123456789ABCDEF";
339 
340 void
make_tex_char(string name,unsigned char c,glyph gl)341 printer_rep::make_tex_char (string name, unsigned char c, glyph gl) {
342   // cout << "Make char " << (int) c << " of " << name << "\n";
343   string char_name (name * "-" * as_string ((int) c));
344   if (tex_chars->contains (char_name)) return;
345   if (!tex_fonts->contains (name)) {
346     tex_fonts (name)= "F" * as_string (nfonts);
347     tex_font_chars (name)= array<int> (0);
348     nfonts++;
349   }
350   tex_font_chars (name) << ((int) c);
351 
352   string hex_code;
353   int i, j, count=0, cur= 0;
354   for (j=0; j < gl->height; j++)
355     for (i=0; i < ((gl->width+7) & (-8)); i++) {
356       cur= cur << 1;
357       if ((i<gl->width) && (gl->get_x(i,j)>0)) cur++;
358       count++;
359       if (count==4) {
360         hex_code << hex_string[cur];
361         cur  = 0;
362         count= 0;
363       }
364     }
365 
366   int d1= gl->width;
367   int d2= gl->height;
368   int d3= 130+ gl->xoff;
369   int d4= 126+ gl->yoff;
370   int d5= gl->lwidth;
371   if ((d1<256) && (d2<256) && (d3<256) && (d4<256) && (d5<256)) {
372     hex_code << as_hexadecimal (d1, 2) << as_hexadecimal (d2, 2)
373 	     << as_hexadecimal (d3, 2) << as_hexadecimal (d4, 2)
374 	     << as_hexadecimal (d5, 2);
375     hex_code= "<" * hex_code * ">";
376   }
377   else {
378     hex_code= "[<" * hex_code * ">";
379     hex_code << as_string (d1) << " " << as_string (d2) << " "
380 	     << as_string (d3) << " " << as_string (d4) << " "
381 	     << as_string (d5) << " ";
382   }
383 
384   tex_chars (char_name)= hex_code;
385   tex_width (char_name)= as_string (d5);
386 }
387 
388 static string
find_ps_font_name(string name,string s)389 find_ps_font_name (string name, string s) {
390   int i, n= N(s);
391   for (i=0; i<n; i++) {
392     if (test (s, i, "/FontName /")) {
393       i += 11;
394       int start= i;
395       while (i<n && s[i] != ' ') i++;
396       return s (start, i);
397     }
398     while (i<n && s[i] != '\12' && s[i] != '\15') i++;
399   }
400   return name;
401 }
402 
403 
404 #define HEX_PER_LINE 30
405 
parse_length(string pfb,int & pos)406 static SI parse_length (string pfb, int& pos) {
407   QN c4= (QN) pfb[pos++];
408   QN c3= (QN) pfb[pos++];
409   QN c2= (QN) pfb[pos++];
410   QI c1= (QI) pfb[pos++];
411   return (((((((SI) c1)<<8)+ ((SI) c2))<<8)+ ((SI) c3))<<8)+ c4;
412 }
413 
pfb_to_pfa(url file)414 static string pfb_to_pfa (url file) {
415   //cout << "pfb_to_pfa :" << file << LF;
416   string pfb, pfa;
417   QN magic, type = 0;
418   SI length;
419 
420   (void) load_string (file, pfb, true);
421   int pos = 0, size = N(pfb);
422   while ((pos < size) && (type != 3)) {
423     parse (pfb, pos, magic);
424     //cout << "magic:" << as_hexadecimal(magic,2) << LF ;
425     if (magic != 128) {
426       FAILED ("Not a pfb file");
427     }
428     parse (pfb, pos, type);
429     //cout << "type:" << as_hexadecimal(type,2) << LF;
430     switch (type) {
431 
432       case 1 :
433         // plain text
434         length = parse_length (pfb, pos);
435         // parse (pfb, pos, length);
436         //cout << "plain text of size " << length << LF;
437         for (int i=0; i <length; i++) {
438           QI ch;
439           parse(pfb, pos, ch);
440           if (ch == '\r') pfa << "\n";
441           else pfa << ch;
442         }
443         break;
444 
445       case 2 :
446         // binary data
447         length = parse_length (pfb, pos);
448         //        parse (pfb, pos, length);
449         //cout << "binary data of size " << length << LF;
450         for (int i=0; i <length; i++) {
451           QI ch;
452           parse(pfb, pos, ch);
453           pfa << as_hexadecimal (ch, 2);
454           if ((i+1) % HEX_PER_LINE == 0) pfa << "\n";
455         }
456         break;
457 
458       case 3 :
459         //cout << "end of file"  << LF;
460         // end of file
461         break;
462 
463       default :
464         FAILED ("Unknown field type while reading PFB file");
465         break;
466 
467     }
468   }
469   return pfa;
470 }
471 
472 #undef HEX_PER_LINE
473 
474 
475 
476 
477 void
generate_tex_fonts()478 printer_rep::generate_tex_fonts () {
479   hashset<string> done;
480   iterator<string> it= iterate (tex_fonts);
481   while (it->busy ()) {
482     string fn_name= it->next ();
483     array<int> a= tex_font_chars [fn_name];
484     merge_sort (a);
485 
486     int i, d, l;
487     string name = tex_fonts [fn_name], ttf;
488     int    pos  = search_forwards (".", fn_name);
489     string root = (pos==-1? fn_name: fn_name (0, pos));
490 #ifndef OS_WIN32 // we need pfbtopfa
491     if ((pos!=-1) && ends (fn_name, "tt")) {
492       int pos2= search_backwards (":", fn_name);
493       root= fn_name (0, pos2);
494       url u= tt_font_find (root);
495       if (suffix (u) == "pfb") {
496         ttf = pfb_to_pfa (u);
497       }
498     }
499 #endif
500 
501     if (ttf != "") {
502       string ttf_name= find_ps_font_name (root, ttf);
503       if (!done->contains (root)) {
504 	prologue << "%%BeginFont: " << root << "\n";
505 	prologue << ttf;
506 	prologue << "\n%%EndFont\n";
507 	done->insert (root);
508       }
509 
510       array<string> cum;
511       cum << "{}" * as_string (N(a));
512       for (i=0; i<N(a); i++) {
513         string w= tex_width [fn_name * "-" * as_string (a[i])];
514         d= (i==0? a[0]: (a[i]-a[i-1]-1));
515         if (d>0) cum << as_string (d) * "[";
516         cum << w * " ";
517       }
518       d= 255-a[i-1];
519       if (d>0) cum << as_string (d) * "[";
520 
521       int szpos = pos-1;
522       while ((szpos>0) && is_numeric (fn_name[szpos-1])) szpos--;
523       double sz = as_double (fn_name (szpos, pos));
524       double dpi= as_double (fn_name (pos+1, N(fn_name)-2));
525       string mag= as_string (83.022 * (sz/10.0) * (dpi/600.0));
526 
527       string fdef;
528       for (i=N(cum)-1; i>=0; i--) fdef << cum[i];
529       fdef= "/" * name * " " * fdef * " " * mag * " /" * ttf_name * " rf";
530       for (i=0, l=0; i<N(fdef); i++, l++)
531         if ((l<70) || (fdef[i]!=' ')) prologue << fdef[i];
532         else { prologue << '\n'; l=-1; }
533       prologue << "\n";
534     }
535     else {
536       prologue << "/" << tex_fonts [fn_name]
537 	       << " " << as_string (N(a))
538 	       << " " << as_string (a[N(a)-1]+1) << " df\n";
539       for (i=0; i<N(a); i++) {
540         int end;
541         string hex_code= tex_chars [fn_name * "-" * as_string (a[i])];
542         for (end=1; end < N(hex_code); end++)
543           if (hex_code[end-1]=='>') break;
544         string after= hex_code (end, N(hex_code));
545         if ((i>0) && (a[i]==(a[i-1]+1))) after << "I";
546         else after << as_string (a[i]) << " D";
547         if (i==(N(a)-1)) after << " E";
548         hex_code= hex_code (0, end);
549 
550         int j, l, n= N(hex_code);
551         for (j=0; j<n; j+=79) {
552           if (n < (j+79)) prologue << hex_code (j, n);
553           else prologue << hex_code (j, j+79) << "\n";
554         }
555         l= 79-(n%79);
556         if (l<N(after)) prologue << "\n";
557         prologue << after << "\n";
558       }
559     }
560   }
561 }
562 
563 /******************************************************************************
564 * Transformed rendering
565 ******************************************************************************/
566 
567 void
set_transformation(frame fr)568 printer_rep::set_transformation (frame fr) {
569   ASSERT (fr->linear, "only linear transformations have been implemented");
570 
571   SI cx1, cy1, cx2, cy2;
572   get_clipping (cx1, cy1, cx2, cy2);
573   rectangle oclip (cx1, cy1, cx2, cy2);
574 
575   frame cv= scaling (point (pixel, -pixel),
576                      point (-ox+dpi*pixel, -oy-dpi*pixel));
577   frame tr= invert (cv) * fr * cv;
578   point o = tr (point (0.0, 0.0));
579   point ux= tr (point (1.0, 0.0)) - o;
580   point uy= tr (point (0.0, 1.0)) - o;
581   //cout << "Set transformation " << o << ", " << ux << ", " << uy << "\n";
582   double tx= o[0];
583   double ty= o[1];
584   print ("gsave");
585   print ("[");
586   print (as_string (ux[0]));
587   print (as_string (ux[1]));
588   print (as_string (uy[0]));
589   print (as_string (uy[1]));
590   print (as_string (tx));
591   print (as_string (ty));
592   print ("]");
593   print ("concat");
594 
595   rectangle nclip= fr [oclip];
596   renderer_rep::clip (nclip->x1, nclip->y1, nclip->x2, nclip->y2);
597 }
598 
599 void
reset_transformation()600 printer_rep::reset_transformation () {
601   renderer_rep::unclip ();
602   print ("grestore");
603 }
604 
605 /******************************************************************************
606 * Clipping
607 ******************************************************************************/
608 
609 void
set_clipping(SI x1,SI y1,SI x2,SI y2,bool restore)610 printer_rep::set_clipping (SI x1, SI y1, SI x2, SI y2, bool restore) {
611   outer_round (x1, y1, x2, y2);
612   renderer_rep::set_clipping (x1, y1, x2, y2);
613   if (restore) {
614     print (PS_CLIP_POP);
615     cfn= "";
616   }
617   else {
618     print (PS_CLIP_PUSH);
619     print (x1, y1);
620     print (x2, y2);
621     print (PS_CLIP);
622   }
623 }
624 
625 /******************************************************************************
626 * graphical routines
627 ******************************************************************************/
628 
629 pencil
get_pencil()630 printer_rep::get_pencil () {
631   return pen;
632 }
633 
634 brush
get_background()635 printer_rep::get_background () {
636   return bgb;
637 }
638 
639 void
set_pencil(pencil pen2)640 printer_rep::set_pencil (pencil pen2) {
641   pen= pen2;
642   color c= pen->get_color ();
643   int r, g, b, a;
644   get_rgb_color (c, r, g, b, a);
645   opacity= a;
646   if (a != 255) {
647     int r2, g2, b2, a2;
648     get_rgb_color (bg, r2, g2, b2, a2);
649     r= (r * a + r2 * (255 - a)) / 255;
650     g= (g * a + g2 * (255 - a)) / 255;
651     b= (b * a + b2 * (255 - a)) / 255;
652     c= rgb_color (r, g, b, 255);
653   }
654   if (c != fg) {
655     fg= c;
656     select_color (fg);
657   }
658   //if (pen->w != lw) {
659   // FIXME: apparently, the line width can be overidden by some of
660   // the graphical constructs (example file: newimpl.tm, in which
661   // the second dag was not printed using the right width)
662   lw= pen->get_width ();
663   select_line_width (lw);
664   //}
665 }
666 
667 void
set_background(brush b)668 printer_rep::set_background (brush b) {
669   //if (bgb==b) return;
670   bgb= b;
671   bg= b->get_color ();
672 }
673 
674 void
draw(int ch,font_glyphs fn,SI x,SI y)675 printer_rep::draw (int ch, font_glyphs fn, SI x, SI y) {
676   if (opacity == 0) return;
677   glyph gl= fn->get(ch);
678   if (is_nil (gl)) return;
679   string name= fn->res_name;
680   unsigned char c= ch;
681   if (ch >= 256) {
682     name= name * "-" * as_string (ch / 256);
683     c= (unsigned char) (ch & 255);
684   }
685   make_tex_char (name, c, gl);
686   select_tex_font (name);
687   move_to (x, y);
688   print ("(" * prepare_text (string ((char) c)) * ")p");
689   tex_flag= true;
690   xpos += gl->lwidth;
691 }
692 
693 void
line(SI x1,SI y1,SI x2,SI y2)694 printer_rep::line (SI x1, SI y1, SI x2, SI y2) {
695   if (opacity == 0) return;
696   print (x1, y1);
697   print (x2, y2);
698   print (PS_LINE);
699 }
700 
701 void
lines(array<SI> x,array<SI> y)702 printer_rep::lines (array<SI> x, array<SI> y) {
703   if (opacity == 0) return;
704   int i, n= N(x);
705   if ((N(y) != n) || (n<1)) return;
706   print (x[0], y[0]);
707   print (PS_POL_START);
708   for (i=1; i<n; i++) {
709     print (x[i], y[i]);
710     print (PS_POL_NEXT);
711   }
712   print (PS_STROKE);
713 }
714 
715 void
clear(SI x1,SI y1,SI x2,SI y2)716 printer_rep::clear (SI x1, SI y1, SI x2, SI y2) {
717   select_color (bg);
718   print (x1, y1);
719   print (x2, y2);
720   print (PS_FILL);
721   select_color (fg);
722 }
723 
724 void
fill(SI x1,SI y1,SI x2,SI y2)725 printer_rep::fill (SI x1, SI y1, SI x2, SI y2) {
726   if (opacity == 0) return;
727   if ((x1<x2) && (y1<y2)) {
728     print (x1, y1);
729     print (x2, y2);
730     print (PS_FILL);
731   }
732 }
733 
734 void
arc(SI x1,SI y1,SI x2,SI y2,int alpha,int delta)735 printer_rep::arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
736   if (opacity == 0) return;
737   print ((x1+x2)/2, (y1+y2)/2);
738   print (as_string ((x2-x1)/(2*PIXEL)));
739   print (as_string ((y1-y2)/(2*PIXEL)));
740   print (as_string (((double) alpha)/64));
741   print (as_string (((double) (alpha+delta))/64));
742   print (PS_ARC);
743 }
744 
745 void
fill_arc(SI x1,SI y1,SI x2,SI y2,int alpha,int delta)746 printer_rep::fill_arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
747   if (opacity == 0) return;
748   print ((x1+x2)/2, (y1+y2)/2);
749   print (as_string ((x2-x1)/(2*PIXEL)));
750   print (as_string ((y1-y2)/(2*PIXEL)));
751   print (as_string (((double) alpha)/64));
752   print (as_string (((double) (alpha+delta))/64));
753   print (PS_FILL_ARC);
754 }
755 
756 void
polygon(array<SI> x,array<SI> y,bool convex)757 printer_rep::polygon (array<SI> x, array<SI> y, bool convex) {
758   if (opacity == 0) return;
759   (void) convex;
760   int i, n= N(x);
761   if ((N(y) != n) || (n<1)) return;
762   print (x[0], y[0]);
763   print (PS_POL_START);
764   for (i=1; i<n; i++) {
765     print (x[i], y[i]);
766     print (PS_POL_NEXT);
767   }
768   print (PS_POL_END);
769 }
770 
771 /*
772 string
773 incorporate_postscript (string s) {
774   int i;
775   string r;
776   for (i=0; i<N(s); )
777     if (s[i] == '%') {
778       for (; (i<N(s)) && (s[i]!='\n'); i++);
779       if (i<N(s)) i++;
780     }
781     else {
782       for (; (i<N(s)) && (s[i]!='\n'); ) r << s[i++];
783       if (i<N(s)) { r << s[i++]; }
784     }
785   return r;
786 }
787 */
788 
789 void
image(string name,string eps,SI x1,SI y1,SI x2,SI y2,SI w,SI h,SI x,SI y,int alpha)790 printer_rep::image (
791   string name, string eps, SI x1, SI y1, SI x2, SI y2,
792   SI w, SI h, SI x, SI y, int alpha)
793 {
794   if (opacity == 0 || alpha == 0) return;
795 
796   double sc_x= (72.0/dpi) * ((double) (w/PIXEL)) / ((double) (x2-x1));
797   double sc_y= (72.0/dpi) * ((double) (h/PIXEL)) / ((double) (y2-y1));
798   cr ();
799   cr ();
800 
801   print (x, y);
802   print ("a");
803   print ("currentpoint");
804   print ("currentpoint");
805   print ("translate");
806   print (as_string (sc_x));
807   print (as_string (sc_y));
808   print ("scale");
809   print ("neg");
810   print ("exch");
811   print ("neg");
812   print ("exch");
813   print ("translate");
814   print (x, y);
815   print ("a");
816   cr ();
817   /* Black Black 248 3155 a currentpoint currentpoint translate
818      0.37114 0.37114 scale neg exch neg exch translate 248 3155 a */
819 
820   print ("@beginspecial");
821   print (as_string (x1));
822   print ("@llx");
823   print (as_string (y1));
824   print ("@lly");
825   print (as_string (x2));
826   print ("@urx");
827   print (as_string (y2));
828   print ("@ury");
829   print (as_string (10*(x2-x1)));
830   print ("@rwi");
831   print ("@clip");
832   print ("@setspecial");
833   cr ();
834   /* @beginspecial 0 @llx 0 @lly 613.291260 @urx 613.291260 @ury 6110 @rwi
835      @clip @setspecial */
836 
837   body << "%%BeginDocument: " << name  << "\n";
838   body << eps; // incorporate_postscript (eps);
839   body << "%%EndDocument";
840   cr ();
841 
842   print ("@endspecial");
843   print (x, y);
844   print ("a");
845   print ("currentpoint");
846   print ("currentpoint");
847   print ("translate");
848   print (as_string (1/sc_x));
849   print (as_string (1/sc_y));
850   print ("scale");
851   print ("neg");
852   print ("exch");
853   print ("neg");
854   print ("exch");
855   print ("translate");
856   print (x, y);
857   print ("a");
858   cr ();
859   cr ();
860 
861   /* @endspecial 248 3155 a currentpoint currentpoint translate
862      1 0.37114 div 1 0.37114 div scale neg exch neg exch translate
863      248 3155 a 660 3073 a ... */
864 
865   (void) w; (void) h;
866 }
867 
868 void
fetch(SI x1,SI y1,SI x2,SI y2,renderer ren,SI x,SI y)869 printer_rep::fetch (SI x1, SI y1, SI x2, SI y2, renderer ren, SI x, SI y) {
870   (void) x1; (void) y1; (void) x2; (void) y2;
871   (void) ren; (void) x; (void) y;
872 }
873 
874 void
new_shadow(renderer & ren)875 printer_rep::new_shadow (renderer& ren) {
876   (void) ren;
877 }
878 
879 void
delete_shadow(renderer & ren)880 printer_rep::delete_shadow (renderer& ren) {
881   (void) ren;
882 }
883 
884 void
get_shadow(renderer ren,SI x1,SI y1,SI x2,SI y2)885 printer_rep::get_shadow (renderer ren, SI x1, SI y1, SI x2, SI y2) {
886   (void) ren; (void) x1; (void) y1; (void) x2; (void) y2;
887 }
888 
889 void
put_shadow(renderer ren,SI x1,SI y1,SI x2,SI y2)890 printer_rep::put_shadow (renderer ren, SI x1, SI y1, SI x2, SI y2) {
891   (void) ren; (void) x1; (void) y1; (void) x2; (void) y2;
892 }
893 
894 void
apply_shadow(SI x1,SI y1,SI x2,SI y2)895 printer_rep::apply_shadow (SI x1, SI y1, SI x2, SI y2) {
896   (void) x1; (void) y1; (void) x2; (void) y2;
897 }
898 
899 renderer
shadow(picture & pic,SI x1,SI y1,SI x2,SI y2)900 printer_rep::shadow (picture& pic, SI x1, SI y1, SI x2, SI y2) {
901   renderer ren= renderer_rep::shadow (pic, x1, y1, x2, y2);
902   ren->set_zoom_factor (1.0);
903   return ren;
904 }
905 
906 void
draw_picture(picture p,SI x,SI y,int alpha)907 printer_rep::draw_picture (picture p, SI x, SI y, int alpha) {
908   (void) alpha; // FIXME
909   int w= p->get_width (), h= p->get_height ();
910   int ox= p->get_origin_x (), oy= p->get_origin_y ();
911   int pixel= 5*PIXEL;
912   string name= "picture";
913   string eps= picture_as_eps (p, 600);
914   int x1= -ox;
915   int y1= -oy;
916   int x2= w - ox;
917   int y2= h - oy;
918   x -= (int) 2.06 * ox * pixel; // FIXME: where does the magic 2.06 come from?
919   y -= (int) 2.06 * oy * pixel;
920   image (name, eps, x1, y1, x2, y2, w * pixel, h * pixel, x, y, 255);
921 }
922 
923 void
draw_scalable(scalable im,SI x,SI y,int alpha)924 printer_rep::draw_scalable (scalable im, SI x, SI y, int alpha) {
925   if (im->get_type () != scalable_image)
926     renderer_rep::draw_scalable (im, x, y, alpha);
927   else {
928     url u= im->get_name ();
929     rectangle r= im->get_logical_extents ();
930     SI w= r->x2, h= r->y2;
931     string ps_image= ps_load (u);
932     string imtext= is_ramdisc (u)? "inline image": as_string (u);
933     int x1, y1, x2, y2;
934     ps_bounding_box (u, x1, y1, x2, y2);
935     image (imtext, ps_image, x1, y1, x2, y2, w, h, x, y, alpha);
936   }
937 }
938 
939 void
anchor(string label,SI x1,SI y1,SI x2,SI y2)940 printer_rep::anchor (string label, SI x1, SI y1, SI x2, SI y2) {
941   string s = "(";
942   s = s << prepare_text (label) << ") cvn";
943   if (linelen>0) cr ();
944   print ("[ /Dest");
945   print (s);
946   print ("/View [/XYZ");
947   print (x1, y1);
948   print ("null] /DEST pdfmark");
949   cr ();
950 }
951 
952 void
href(string label,SI x1,SI y1,SI x2,SI y2)953 printer_rep::href (string label, SI x1, SI y1, SI x2, SI y2) {
954   bool preserve= (get_locus_rendering ("locus-on-paper") == "preserve");
955   if (linelen>0) cr ();
956   print ("[");
957   if (starts (label, "#")) {
958     print ("/Dest");
959     print ("(" * prepare_text (label) * ") cvn");
960   }
961   else {
962     print ("/A");
963     print ("<< /S /URI /URI (" * prepare_text (label) * ") >>");
964   }
965   print ("/Rect [");
966   print (x1 - 5*PIXEL, y1 - 10*PIXEL);
967   print (x2 + 5*PIXEL, y2 + 10*PIXEL);
968   print ("]");
969   if (preserve)
970     print ("/Border [16 16 1 [3 10]] /Color [0.75 0.5 1.0]");
971   else
972     print ("/Border [16 16 0 [3 10]] /Color [0.75 0.5 1.0]");
973   print ("/Subtype /Link");
974   print ("/ANN pdfmark");
975   cr ();
976 }
977 
978 void
toc_entry(string kind,string title,SI x,SI y)979 printer_rep::toc_entry (string kind, string title, SI x, SI y) {
980   decode (x, y);
981   string ls= "1";
982   if (kind == "toc-strong-1") ls= "1";
983   if (kind == "toc-strong-2") ls= "2";
984   if (kind == "toc-1") ls= "3";
985   if (kind == "toc-2") ls= "4";
986   if (kind == "toc-3") ls= "5";
987   if (kind == "toc-4") ls= "6";
988   if (kind == "toc-5") ls= "7";
989   string ps= as_string (cur_page);
990   string xs= as_string (x-dpi);
991   string ys= as_string (y-dpi);
992   toc << tuple (title, ls, ps, xs, ys);
993 }
994 
995 void
structure_toc(tree t,int & i,tree & out,string level)996 structure_toc (tree t, int& i, tree& out, string level) {
997   //cout << "Structure " << t[i] << " at " << level << "\n";
998   if (i >= N(t)) return;
999   string nlevel= t[i][1]->label;
1000   if (nlevel <= level) return;
1001   tree next= tuple (t[i]);
1002   i++;
1003   while (i < N(t)) {
1004     string slevel= t[i][1]->label;
1005     if (slevel <= nlevel) break;
1006     structure_toc (t, i, next, nlevel);
1007   }
1008   out << next;
1009 }
1010 
1011 static string
to_hex_string(string s)1012 to_hex_string (string s) {
1013   return "<FEFF" * utf8_to_hex_string (cork_to_utf8 (s)) * ">";
1014 }
1015 
1016 void
generate_toc_item(tree t)1017 printer_rep::generate_toc_item (tree t) {
1018   string title= t[0][0]->label;
1019   string level= t[0][1]->label;
1020   string page = t[0][2]->label;
1021   string x    = t[0][3]->label;
1022   string y    = t[0][4]->label;
1023   string htit = to_hex_string (title);
1024   print ("[");
1025   if (N(t) > 1) {
1026     print ("/Count");
1027     print (as_string (1 - N(t)));
1028   }
1029   print ("/Page");
1030   print (page);
1031   print ("/View [ /XYZ");
1032   print (x);
1033   print (y);
1034   print ("0");
1035   print ("]");
1036   print ("/Title " * htit);
1037   print ("/OUT pdfmark");
1038   cr ();
1039   for (int i=1; i<N(t); i++)
1040     generate_toc_item (t[i]);
1041 }
1042 
1043 void
generate_toc()1044 printer_rep::generate_toc () {
1045   tree rew (TUPLE);
1046   int i=0;
1047   while (i < N(toc))
1048     structure_toc (toc, i, rew, "0");
1049   for (i=0; i<N(rew); i++)
1050     generate_toc_item (rew[i]);
1051 }
1052 
1053 void
set_metadata(string kind,string val)1054 printer_rep::set_metadata (string kind, string val) {
1055   metadata (kind)= val;
1056 }
1057 
1058 void
generate_metadata()1059 printer_rep::generate_metadata () {
1060   if (N(metadata) == 0) return;
1061   print ("[");
1062   if (metadata->contains ("title"))
1063     print ("/Title " * to_hex_string (metadata ["title"]));
1064   if (metadata->contains ("author"))
1065     print ("/Author " * to_hex_string (metadata ["author"]));
1066   if (metadata->contains ("subject"))
1067     print ("/Subject " * to_hex_string (metadata ["subject"]));
1068   print ("/DOCINFO pdfmark");
1069 }
1070 
1071 /******************************************************************************
1072 * user interface
1073 ******************************************************************************/
1074 
1075 renderer
printer(url ps_file_name,int dpi,int nr_pages,string page_type,bool landscape,double paper_w,double paper_h)1076 printer (url ps_file_name, int dpi, int nr_pages,
1077 	 string page_type, bool landscape, double paper_w, double paper_h)
1078 {
1079   page_type= as_string (call ("standard-paper-size", object (page_type)));
1080   return tm_new<printer_rep> (ps_file_name, dpi, nr_pages,
1081                               page_type, landscape, paper_w, paper_h);
1082 }
1083