fix copyright statements
[mplib] / src / texk / web2c / mpdir / psout.w
1 % $Id$
2 %
3 % Copyright 2008 Taco Hoekwater.
4 %
5 % This program is free software: you can redistribute it and/or modify
6 % it under the terms of the GNU General Public License as published by
7 % the Free Software Foundation, either version 2 of the License, or
8 % (at your option) any later version.
9 %
10 % This program is distributed in the hope that it will be useful,
11 % but WITHOUT ANY WARRANTY; without even the implied warranty of
12 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 % GNU General Public License for more details.
14 %
15 % You should have received a copy of the GNU General Public License
16 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 %
18 % TeX is a trademark of the American Mathematical Society.
19 % METAFONT is a trademark of Addison-Wesley Publishing Company.
20 % PostScript is a trademark of Adobe Systems Incorporated.
21
22 % Here is TeX material that gets inserted after \input webmac
23 \def\hang{\hangindent 3em\noindent\ignorespaces}
24 \def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
25 \def\PASCAL{Pascal}
26 \def\ps{PostScript}
27 \def\ph{\hbox{Pascal-H}}
28 \def\psqrt#1{\sqrt{\mathstrut#1}}
29 \def\k{_{k+1}}
30 \def\pct!{{\char`\%}} % percent sign in ordinary text
31 \font\tenlogo=logo10 % font used for the METAFONT logo
32 \font\logos=logosl10
33 \def\MF{{\tenlogo META}\-{\tenlogo FONT}}
34 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
35 \def\<#1>{$\langle#1\rangle$}
36 \def\section{\mathhexbox278}
37 \let\swap=\leftrightarrow
38 \def\round{\mathop{\rm round}\nolimits}
39 \mathchardef\vb="026A % synonym for `\|'
40 \def\[#1]{} % from pascal web
41 \def\(#1){} % this is used to make section names sort themselves better
42 \def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
43
44 \let\?=\relax % we want to be able to \write a \?
45
46 \def\title{MetaPost \ps\ output}
47 \def\topofcontents{\hsize 5.5in
48   \vglue -30pt plus 1fil minus 1.5in
49   \def\?##1]{\hbox to 1in{\hfil##1.\ }}
50   }
51 \def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
52 \pdfoutput=1
53 \pageno=3
54
55
56 @d true 1
57 @d false 0
58 @d null_font 0
59 @d null 0
60 @d unity   0200000 /* $2^{16}$, represents 1.00000 */
61 @d el_gordo   017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
62 @d incr(A)   (A)=(A)+1 /* increase a variable by unity */
63 @d decr(A)   (A)=(A)-1 /* decrease a variable by unity */
64 @d negate(A)   (A)=-(A) /* change the sign of a variable */
65 @d odd(A)   ((A)%2==1)
66 @d half(A) ((A)/2)
67 @d print_err(A) mp_print_err(mp,(A))
68 @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
69
70 @c
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <stdarg.h>
75 #include <assert.h>
76 #include "avl.h"
77 #include "mplib.h"
78 #include "psout.h" /* external header */
79 #include "mpmp.h" /* internal header */
80 #include "mppsout.h" /* internal header */
81 @h
82 @<Declarations@>
83 @<Static variables in the outer block@>
84
85 @ There is a small bit of code from the backend that bleads through
86 to the frontend because I do not know how to set up the includes
87 properly. Those are the definitions of |struct libavl_allocator|
88 and |typedef struct psout_data_struct * psout_data|.
89
90 The |libavl_allocator| is a trick that makes sure that frontends 
91 do not need |avl.h|, and the |psout_data| is needed for the backend 
92 data structure.
93
94 @ @(mppsout.h@>=
95 @<Types...@>
96 typedef struct psout_data_struct {
97   @<Globals@>
98 } psout_data_struct ;
99 @<Exported function headers@>
100
101 @ @<Exported function headers@>=
102 void mp_backend_initialize (MP mp) ;
103 void mp_backend_free (MP mp) ;
104
105 @
106 @c void mp_backend_initialize (MP mp) {
107   mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
108   @<Set initial values@>;
109 }
110 void mp_backend_free (MP mp) {
111   @<Dealloc variables@>;
112   enc_free(mp);
113   t1_free(mp);
114   fm_free(mp);
115   mp_xfree(mp->ps);
116   mp->ps = NULL;
117 }
118
119 @ Writing to ps files
120
121 @<Globals@>=
122 integer ps_offset;
123   /* the number of characters on the current \ps\ file line */
124
125 @ @<Set initial values@>=
126 mp->ps->ps_offset = 0;
127
128 @
129
130 @d wps(A)     (mp->write_ascii_file)(mp,mp->ps_file,(A))
131 @d wps_chr(A) do { 
132   char ss[2]; 
133   ss[0]=(A); ss[1]=0; 
134   (mp->write_ascii_file)(mp,mp->ps_file,(char *)ss); 
135 } while (0)
136 @d wps_cr     (mp->write_ascii_file)(mp,mp->ps_file,"\n")
137 @d wps_ln(A)  { wterm_cr; (mp->write_ascii_file)(mp,mp->ps_file,(A)); }
138
139 @c
140 void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
141   wps_cr; 
142   mp->ps->ps_offset=0;
143
144
145 @ @c
146 void mp_ps_print_char (MP mp, ASCII_code s) { /* prints a single character */
147   if ( s==13 ) {
148     wps_cr; mp->ps->ps_offset=0;
149   } else {
150     wps_chr(s); incr(mp->ps->ps_offset);
151   }
152 }
153
154 @ @c
155 void mp_ps_do_print (MP mp, const char *ss, unsigned int len) { /* prints string |s| */
156   unsigned int j = 0;
157   while ( j<len ){ 
158     mp_ps_print_char(mp, ss[j]); incr(j);
159   }
160 }
161
162 @ Deciding where to break the ps output line. 
163
164 @d ps_room(A) if ( (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) {
165   mp_ps_print_ln(mp); /* optional line break */
166 }
167
168 @c
169 void mp_ps_print (MP mp, const char *ss) {
170   ps_room(strlen(ss));
171   mp_ps_do_print(mp, ss, strlen(ss));
172 }
173
174 @ The procedure |print_nl| is like |print|, but it makes sure that the
175 string appears at the beginning of a new line.
176
177 @c
178 void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */
179   if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
180   mp_ps_print(mp, s);
181 }
182
183 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
184
185 @c
186 void mp_ps_print_the_digs (MP mp, eight_bits k) {
187   /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
188   while ( k-->0 ){ 
189     mp_ps_print_char(mp, '0'+mp->dig[k]);
190   }
191 }
192
193 @ The following procedure, which prints out the decimal representation of a
194 given integer |n|, has been written carefully so that it works properly
195 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
196 to negative arguments, since such operations are not implemented consistently
197 by all \PASCAL\ compilers.
198
199 @c
200 void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */
201   integer m; /* used to negate |n| in possibly dangerous cases */
202   int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
203   if ( n<0 ) { 
204     mp_ps_print_char(mp, '-');
205     if ( n>-100000000 ) {
206           negate(n);
207     } else  { 
208           m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
209       if ( m<10 ) {
210         mp->dig[0]=m;
211       } else { 
212         mp->dig[0]=0; incr(n);
213       }
214     }
215   }
216   do {  
217     mp->dig[k]=n % 10; n=n / 10; incr(k);
218   } while (n!=0);
219   mp_ps_print_the_digs(mp, k);
220 }
221
222 @ @<Internal ...@>=
223 void mp_ps_print_int (MP mp,integer n);
224
225 @ \MP\ also makes use of a trivial procedure to print two digits. The
226 following subroutine is usually called with a parameter in the range |0<=n<=99|.
227
228 @c 
229 void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */
230   n=abs(n) % 100; 
231   mp_ps_print_char(mp, '0'+(n / 10));
232   mp_ps_print_char(mp, '0'+(n % 10));
233 }
234
235 @ Conversely, here is a procedure analogous to |print_int|. If the output
236 of this procedure is subsequently read by \MP\ and converted by the
237 |round_decimals| routine above, it turns out that the original value will
238 be reproduced exactly. A decimal point is printed only if the value is
239 not an integer. If there is more than one way to print the result with
240 the optimum number of digits following the decimal point, the closest
241 possible value is given.
242
243 The invariant relation in the \&{repeat} loop is that a sequence of
244 decimal digits yet to be printed will yield the original number if and only if
245 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
246 We can stop if and only if $f=0$ satisfies this condition; the loop will
247 terminate before $s$ can possibly become zero.
248
249 @c
250 void mp_ps_print_scaled (MP mp,scaled s) { 
251   scaled delta; /* amount of allowable inaccuracy */
252   if ( s<0 ) { 
253         mp_ps_print_char(mp, '-'); 
254     negate(s); /* print the sign, if negative */
255   }
256   mp_ps_print_int(mp, s / unity); /* print the integer part */
257   s=10*(s % unity)+5;
258   if ( s!=5 ) { 
259     delta=10; 
260     mp_ps_print_char(mp, '.');
261     do {  
262       if ( delta>unity )
263         s=s+0100000-(delta / 2); /* round the final digit */
264       mp_ps_print_char(mp, '0'+(s / unity)); 
265       s=10*(s % unity); 
266       delta=delta*10;
267     } while (s>delta);
268   }
269 }
270
271
272 @* \[44a] Dealing with font encodings.
273
274 First, here are a few helpers for parsing files
275
276 @d check_buf(size, buf_size)
277     if ((unsigned)(size) > (unsigned)(buf_size)) {
278       char S[128];
279       mp_snprintf(S,128,"buffer overflow: (%d,%d) at file %s, line %d",
280                size,buf_size, __FILE__,  __LINE__ );
281       mp_fatal_error(mp,S);
282     }
283
284 @d append_char_to_buf(c, p, buf, buf_size) do {
285     if (c == 9)
286         c = 32;
287     if (c == 13 || c == EOF)
288         c = 10;
289     if (c != ' ' || (p > buf && p[-1] != 32)) {
290         check_buf(p - buf + 1, (buf_size));
291         *p++ = c; 
292     }
293 } while (0)
294
295 @d append_eol(p, buf, buf_size) do {
296     check_buf(p - buf + 2, (buf_size));
297     if (p - buf > 1 && p[-1] != 10)
298         *p++ = 10;
299     if (p - buf > 2 && p[-2] == 32) {
300         p[-2] = 10;
301         p--;
302     }
303     *p = 0;
304 } while (0)
305
306 @d remove_eol(p, buf) do {
307     p = strend(buf) - 1;
308     if (*p == 10)
309         *p = 0;
310 } while (0)
311
312 @d skip(p, c)   if (*p == c)  p++
313 @d strend(s)    strchr(s, 0)
314 @d str_prefix(s1, s2)  (strncmp((s1), (s2), strlen(s2)) == 0)
315
316
317 @ @<Types...@>=
318 typedef struct {
319     boolean loaded;             /* the encoding has been loaded? */
320     char *file_name;                 /* encoding file name */
321     char *enc_name;              /* encoding true name */
322     integer objnum;             /* object number */
323     char **glyph_names;
324     integer tounicode;          /* object number of associated ToUnicode entry */
325 } enc_entry;
326
327
328
329
330 @d ENC_STANDARD  0
331 @d ENC_BUILTIN   1
332
333 @<Glob...@>=
334 #define ENC_BUF_SIZE  0x1000
335 char enc_line[ENC_BUF_SIZE];
336 void * enc_file;
337
338
339 @d enc_eof()       (mp->eof_file)(mp,mp->ps->enc_file)
340 @d enc_close()     (mp->close_file)(mp,mp->ps->enc_file)
341
342 @c
343 int enc_getchar(MP mp) {
344   size_t len = 1;
345   unsigned char abyte=0;
346   void *byte_ptr = &abyte;  
347   (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len);
348   return abyte;
349 }
350
351 @ @c 
352 static boolean mp_enc_open (MP mp, char *n) {
353   mp->ps->enc_file=(mp->open_file)(mp,n, "r", mp_filetype_encoding);
354   if (mp->ps->enc_file!=NULL)
355     return true;
356   else
357    return false;
358 }
359 static void mp_enc_getline (MP mp) {
360   char *p;
361   int c;
362 RESTART:
363   if (enc_eof ()) {
364     print_err("unexpected end of file");
365     mp_error(mp);
366   }
367   p = mp->ps->enc_line;
368   do {
369     c = enc_getchar (mp);
370     append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
371   } while (c != 10);
372   append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
373   if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
374     goto RESTART;
375 }
376 static void mp_load_enc (MP mp, char *enc_name, 
377                   char **enc_encname, char **glyph_names){
378   char buf[ENC_BUF_SIZE], *p, *r;
379   int names_count;
380   char *myname;
381   int save_selector = mp->selector;
382   if (!mp_enc_open (mp,enc_name)) {
383       mp_print (mp,"cannot open encoding file for reading");
384       return;
385   }
386   mp_normalize_selector(mp);
387   mp_print (mp,"{");
388   mp_print (mp, enc_name);
389   mp_enc_getline (mp);
390   if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
391     remove_eol (r, mp->ps->enc_line);
392     print_err ("invalid encoding vector (a name or `[' missing): `");
393     mp_print(mp,mp->ps->enc_line);
394     mp_print(mp,"'");
395     mp_error(mp);
396   }
397   while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
398   myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
399   memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
400   *(myname+(r-mp->ps->enc_line-1))=0;
401   *enc_encname = myname;
402   while (*r!='[') r++;
403   r++;                        /* skip '[' */
404   names_count = 0;
405   skip (r, ' ');
406   for (;;) {
407     while (*r == '/') {
408       for (p = buf, r++;
409            *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
410         *p = 0;
411       skip (r, ' ');
412       if (names_count > 256) {
413         print_err ("encoding vector contains more than 256 names");
414         mp_error(mp);
415       }
416       if (mp_xstrcmp (buf, notdef) != 0)
417         glyph_names[names_count] = mp_xstrdup (mp,buf);
418       names_count++;
419     }
420     if (*r != 10 && *r != '%') {
421       if (str_prefix (r, "] def"))
422         goto DONE;
423       else {
424         remove_eol (r, mp->ps->enc_line);
425         print_err
426           ("invalid encoding vector: a name or `] def' expected: `");
427         mp_print(mp,mp->ps->enc_line);
428         mp_print(mp,"'");
429         mp_error(mp);
430       }
431     }
432     mp_enc_getline (mp);
433     r = mp->ps->enc_line;
434   }
435 DONE:
436   enc_close ();
437   mp_print (mp,"}");
438   mp->selector = save_selector;
439 }
440 static void mp_read_enc (MP mp, enc_entry * e) {
441     if (e->loaded)
442         return;
443     e->enc_name = NULL;
444     mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
445     e->loaded = true;
446 }
447
448 @ |write_enc| is used to write either external encoding (given in map file) or
449  internal encoding (read from the font file); 
450  the 2nd argument is a pointer to the encoding entry; 
451  
452 @c
453 static void mp_write_enc (MP mp, enc_entry * e) {
454     int i;
455     int s;
456     int foffset;
457     char **g;
458     if (e->objnum != 0)     /* the encoding has been written already */
459        return;
460      e->objnum = 1;
461      g = e->glyph_names;
462
463     mp_ps_print(mp,"\n%%%%BeginResource: encoding ");
464     mp_ps_print(mp, e->enc_name);
465     mp_ps_print_nl(mp, "/");
466     mp_ps_print(mp, e->enc_name);
467     mp_ps_print(mp, " [ ");
468     foffset = strlen(e->file_name)+3;
469     for (i = 0; i < 256; i++) {
470       s = strlen(g[i]);
471       if (s+1+foffset>=80) {
472             mp_ps_print_ln (mp);
473         foffset = 0;
474       }
475       foffset += s+2;
476       mp_ps_print_char(mp,'/');
477       mp_ps_print(mp, g[i]);
478       mp_ps_print_char(mp,' ');
479     }
480     if (foffset>75)
481            mp_ps_print_ln (mp);
482     mp_ps_print_nl (mp,"] def\n");
483     mp_ps_print(mp,"%%%%EndResource");
484 }
485
486
487 @ All encoding entries go into AVL tree for fast search by name.
488
489 @<Glob...@>=
490 struct avl_table *enc_tree;
491
492 @ Memory management functions for avl 
493
494 @<Static variables in the outer block@>=
495 static const char notdef[] = ".notdef";
496
497 @ @<Declarations@>=
498 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
499 static void avl_xfree (struct libavl_allocator *allocator, void *block);
500
501 @ @c
502 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
503     (void)allocator;
504     return malloc (size);
505 }
506 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
507     (void)allocator;
508     free (block);
509 }
510
511 @ @<Glob...@>=
512 struct libavl_allocator avl_xallocator;
513
514 @ @<Set initial...@>=
515 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
516 mp->ps->avl_xallocator.libavl_free= avl_xfree;
517 mp->ps->enc_tree = NULL;
518
519 @ @c
520 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
521     (void)p;
522     return strcmp (((const enc_entry *) pa)->file_name,
523                    ((const enc_entry *) pb)->file_name);
524 }
525 static enc_entry * mp_add_enc (MP mp, char *s) {
526     int i;
527     enc_entry tmp, *p;
528     void **aa;
529     if (mp->ps->enc_tree == NULL) {
530       mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
531     }
532     tmp.file_name = s;
533     p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
534     if (p != NULL)              /* encoding already registered */
535         return p;
536     p = mp_xmalloc (mp,1,sizeof (enc_entry));
537     p->loaded = false;
538     p->file_name = mp_xstrdup (mp,s);
539     p->objnum = 0;
540     p->tounicode = 0;
541     p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
542     for (i = 0; i < 256; i++)
543         p->glyph_names[i] = (char *) notdef;
544     aa = avl_probe (mp->ps->enc_tree, p);
545     return p;
546 }
547
548 @ cleaning up... 
549
550 @c 
551 static void mp_destroy_enc_entry (void *pa, void *pb) {
552     enc_entry *p;
553     int i;
554     p = (enc_entry *) pa;
555     (void)pb;
556     mp_xfree (p->file_name);
557     if (p->glyph_names != NULL)
558         for (i = 0; i < 256; i++)
559             if (p->glyph_names[i] != notdef)
560                 mp_xfree (p->glyph_names[i]);
561     mp_xfree (p->glyph_names);
562     mp_xfree (p);
563 }
564
565 @ @<Declarations@>=
566 static void enc_free (MP mp);
567
568 @ @c static void enc_free (MP mp) {
569     if (mp->ps->enc_tree != NULL)
570       avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
571 }
572
573 @ @<Exported function headers@>=
574 void mp_reload_encodings (MP mp) ;
575
576 @ @<Declarations@>=
577 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) ;
578
579 @ @c void mp_reload_encodings (MP mp) {
580   int f;
581   enc_entry *e;
582   fm_entry *fm_cur;
583   int lastfnum = mp->last_fnum;
584   for (f=null_font+1;f<=lastfnum;f++) {
585     if (mp->font_enc_name[f]!=NULL ) {
586        mp_xfree(mp->font_enc_name[f]);
587        mp->font_enc_name[f]=NULL;
588     }
589     if (mp_has_fm_entry (mp,f,&fm_cur)) { 
590       if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
591         e = fm_cur->encoding;
592         mp_read_enc (mp,e);
593       }
594     }
595   }
596 }
597 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) {
598   int f;
599   enc_entry *e;
600   fm_entry *fm;
601   for (f=null_font+1;f<=lastfnum;f++) {
602     if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) { 
603       if (fm != NULL && (fm->ps_name != NULL)) {
604         if (is_reencoded (fm)) {
605           if (encodings_only || (!is_subsetted (fm))) {
606             e = fm->encoding;
607             mp_write_enc (mp, e);
608             /* clear for next run */
609             e->objnum = 0;
610           }
611         }
612       }
613     }
614   }
615 }
616
617 @* \[44b] Parsing font map files.
618
619 @d FM_BUF_SIZE     1024
620
621 @<Glob...@>=
622 void * fm_file;
623
624 @
625 @d fm_close()      (mp->close_file)(mp,mp->ps->fm_file)
626 @d fm_eof()        (mp->eof_file)(mp,mp->ps->fm_file)
627
628 @c
629 int fm_getchar(MP mp) {
630   size_t len = 1;
631   unsigned char abyte=0;
632   void *byte_ptr = &abyte;  
633   (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
634   if (len==0)
635     return EOF;
636   return abyte;
637 }
638
639
640 @ @<Types...@>=
641 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
642 enum _ltype { MAPFILE, MAPLINE };
643 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
644 typedef struct mitem {
645     int mode;                   /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
646     int type;                   /* map file or map line */
647     char *map_line;              /* pointer to map file name or map line */
648     int lineno;                 /* line number in map file */
649 } mapitem;
650
651 @ @<Glob...@>=
652 mapitem *mitem;
653 fm_entry *fm_cur;
654 fm_entry *loaded_tfm_found;
655 fm_entry *avail_tfm_found;
656 fm_entry *non_tfm_found;
657 fm_entry *not_avail_tfm_found;
658
659 @ @<Set initial...@>=
660 mp->ps->mitem = NULL;
661
662 @ @<Declarations@>=
663 static const char nontfm[] = "<nontfm>";
664
665 @
666 @d read_field(r, q, buf) do {
667     q = buf;
668     while (*r != ' ' && *r != '\0')
669         *q++ = *r++;
670     *q = '\0';
671     skip (r, ' ');
672 } while (0)
673
674 @d set_field(F) do {
675     if (q > buf)
676         fm->F = mp_xstrdup(mp,buf);
677     if (*r == '\0')
678         goto DONE;
679 } while (0)
680
681 @d cmp_return(a, b)
682     if (a > b)
683         return 1;
684     if (a < b)
685         return -1
686
687 @c
688 static fm_entry *new_fm_entry (MP mp) {
689     fm_entry *fm;
690     fm = mp_xmalloc (mp,1,sizeof(fm_entry));
691     fm->tfm_name = NULL;
692     fm->ps_name = NULL;
693     fm->flags = 4;
694     fm->ff_name = NULL;
695     fm->subset_tag = NULL;
696     fm->encoding = NULL;
697     fm->tfm_num = null_font;
698     fm->tfm_avail = TFM_UNCHECKED;
699     fm->type = 0;
700     fm->slant = 0;
701     fm->extend = 0;
702     fm->ff_objnum = 0;
703     fm->fn_objnum = 0;
704     fm->fd_objnum = 0;
705     fm->charset = NULL;
706     fm->all_glyphs = false;
707     fm->links = 0;
708     fm->pid = -1;
709     fm->eid = -1;
710     return fm;
711 }
712
713 static void delete_fm_entry (fm_entry * fm) {
714     mp_xfree (fm->tfm_name);
715     mp_xfree (fm->ps_name);
716     mp_xfree (fm->ff_name);
717     mp_xfree (fm->subset_tag);
718     mp_xfree (fm->charset);
719     mp_xfree (fm);
720 }
721
722 static ff_entry *new_ff_entry (MP mp) {
723     ff_entry *ff;
724     ff = mp_xmalloc (mp,1,sizeof(ff_entry));
725     ff->ff_name = NULL;
726     ff->ff_path = NULL;
727     return ff;
728 }
729
730 static void delete_ff_entry (ff_entry * ff) {
731     mp_xfree (ff->ff_name);
732     mp_xfree (ff->ff_path);
733     mp_xfree (ff);
734 }
735
736 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
737     static char buf[SMALL_BUF_SIZE];
738     char *p = tfmname, *r = strend (p) - 1, *q = r;
739     while (q > p && isdigit (*q))
740         --q;
741     if (!(q > p) || q == r || (*q != '+' && *q != '-'))
742         return NULL;
743     check_buf (q - p + 1, SMALL_BUF_SIZE);
744     strncpy (buf, p, (size_t) (q - p));
745     buf[q - p] = '\0';
746     *i = atoi (q);
747     return buf;
748 }
749
750 @ @<Exported function headers@>=
751 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
752
753 @ @c
754 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
755     fm_entry *res = NULL;
756     res = mp_fm_lookup (mp, f);
757     if (fm != NULL) {
758        *fm =res;
759     }
760     return (res != NULL);
761 }
762
763 @ @<Glob...@>=
764 struct avl_table *tfm_tree;
765 struct avl_table *ps_tree;
766 struct avl_table *ff_tree;
767
768 @ @<Set initial...@>=
769 mp->ps->tfm_tree = NULL;
770 mp->ps->ps_tree = NULL;
771 mp->ps->ff_tree = NULL;
772
773 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
774
775 @c
776 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
777     (void)p;
778     return strcmp (((const fm_entry *) pa)->tfm_name,
779                    ((const fm_entry *) pb)->tfm_name);
780 }
781
782 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
783
784 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
785     int i;
786     const fm_entry *p1 = (const fm_entry *) pa;
787     const fm_entry *p2 = (const fm_entry *) pb;
788     (void)p;
789     assert (p1->ps_name != NULL && p2->ps_name != NULL);
790     if ((i = strcmp (p1->ps_name, p2->ps_name)))
791         return i;
792     cmp_return (p1->slant, p2->slant);
793     cmp_return (p1->extend, p2->extend);
794     if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
795         (i = strcmp (p1->tfm_name, p2->tfm_name)))
796         return i;
797     return 0;
798 }
799
800 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
801
802 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
803     (void)p;
804     return strcmp (((const ff_entry *) pa)->ff_name,
805                    ((const ff_entry *) pb)->ff_name);
806 }
807
808 @ @c static void create_avl_trees (MP mp) {
809     if (mp->ps->tfm_tree == NULL) {
810         mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
811         assert (mp->ps->tfm_tree != NULL);
812     }
813     if (mp->ps->ps_tree == NULL) {
814         mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
815         assert (mp->ps->ps_tree != NULL);
816     }
817     if (mp->ps->ff_tree == NULL) {
818         mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
819         assert (mp->ps->ff_tree != NULL);
820     }
821 }
822
823 @ The function |avl_do_entry| is not completely symmetrical with regards
824 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
825 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
826 with the original version.
827
828 @d LINK_TFM            0x01
829 @d LINK_PS             0x02
830 @d set_tfmlink(fm)     ((fm)->links |= LINK_TFM)
831 @d set_pslink(fm)      ((fm)->links |= LINK_PS)
832 @d unset_tfmlink(fm)   ((fm)->links &= ~LINK_TFM)
833 @d unset_pslink(fm)    ((fm)->links &= ~LINK_PS)
834 @d has_tfmlink(fm)     ((fm)->links & LINK_TFM)
835 @d has_pslink(fm)      ((fm)->links & LINK_PS)
836
837 @c
838 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
839     fm_entry *p;
840     void *a;
841     void **aa;
842     char s[128];
843
844     /* handle |tfm_name| link */
845
846     if (strcmp (fp->tfm_name, nontfm)) {
847         p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
848         if (p != NULL) {
849             if (mode == FM_DUPIGNORE) {
850                mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
851                      fp->tfm_name);
852                 mp_warn(mp,s);
853                 goto exit;
854             } else {            /* mode == |FM_REPLACE| / |FM_DELETE| */
855                 if (mp_has_font_size(mp,p->tfm_num)) {
856                     mp_snprintf(s,128,
857                         "fontmap entry for `%s' has been used, replace/delete not allowed",
858                          fp->tfm_name);
859                     mp_warn(mp,s);
860                     goto exit;
861                 }
862                 a = avl_delete (mp->ps->tfm_tree, p);
863                 assert (a != NULL);
864                 unset_tfmlink (p);
865                 if (!has_pslink (p))
866                     delete_fm_entry (p);
867             }
868         }
869         if (mode != FM_DELETE) {
870             aa = avl_probe (mp->ps->tfm_tree, fp);
871             assert (aa != NULL);
872             set_tfmlink (fp);
873         }
874     }
875
876     /* handle |ps_name| link */
877
878     if (fp->ps_name != NULL) {
879         assert (fp->tfm_name != NULL);
880         p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
881         if (p != NULL) {
882             if (mode == FM_DUPIGNORE) {
883                 mp_snprintf(s,128,
884                     "ps_name entry for `%s' already exists, duplicates ignored",
885                      fp->ps_name);
886                 mp_warn(mp,s);
887                 goto exit;
888             } else {            /* mode == |FM_REPLACE| / |FM_DELETE| */
889                 if (mp_has_font_size(mp,p->tfm_num)) {
890                     /* REPLACE/DELETE not allowed */
891                     mp_snprintf(s,128,
892                         "fontmap entry for `%s' has been used, replace/delete not allowed",
893                          p->tfm_name);
894                     mp_warn(mp,s);
895                     goto exit;
896                 }
897                 a = avl_delete (mp->ps->ps_tree, p);
898                 assert (a != NULL);
899                 unset_pslink (p);
900                 if (!has_tfmlink (p))
901                     delete_fm_entry (p);
902             }
903         }
904         if (mode != FM_DELETE) {
905             aa = avl_probe (mp->ps->ps_tree, fp);
906             assert (aa != NULL);
907             set_pslink (fp);
908         }
909     }
910   exit:
911     if (!has_tfmlink (fp) && !has_pslink (fp))  /* e. g. after |FM_DELETE| */
912         return 1;               /* deallocation of |fm_entry| structure required */
913     else
914         return 0;
915 }
916
917 @ consistency check for map entry, with warn flag 
918
919 @c
920 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
921     int a = 0;
922     char s[128];
923     assert (fm != NULL);
924     if (fm->ps_name != NULL) {
925         if (is_basefont (fm)) {
926             if (is_fontfile (fm) && !is_included (fm)) {
927                 if (warn) {
928                     mp_snprintf(s,128, "invalid entry for `%s': "
929                          "font file must be included or omitted for base fonts",
930                          fm->tfm_name);
931                     mp_warn(mp,s);
932                 }
933                 a += 1;
934             }
935         } else {                /* not a base font */
936             /* if no font file given, drop this entry */
937             /* |if (!is_fontfile (fm)) {
938                  if (warn) {
939                    mp_snprintf(s,128, 
940                         "invalid entry for `%s': font file missing",
941                                                 fm->tfm_name);
942                     mp_warn(mp,s);
943                  }
944                 a += 2;
945             }|
946             */
947         }
948     }
949     if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
950         if (warn) {
951             mp_snprintf(s,128, 
952                 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
953                  fm->tfm_name);
954                     mp_warn(mp,s);
955         }
956         a += 4;
957     }
958     if ((fm->slant != 0 || fm->extend != 0) &&
959         (is_truetype (fm))) {
960         if (warn) { 
961            mp_snprintf(s,128, 
962                  "invalid entry for `%s': " 
963                  "SlantFont/ExtendFont can be used only with embedded T1 fonts",
964                  fm->tfm_name);
965                     mp_warn(mp,s);
966         }
967         a += 8;
968     }
969     if (abs (fm->slant) > 1000) {
970         if (warn) {
971             mp_snprintf(s,128, 
972                 "invalid entry for `%s': too big value of SlantFont (%g)",
973                  fm->tfm_name, fm->slant / 1000.0);
974                     mp_warn(mp,s);
975         }
976         a += 16;
977     }
978     if (abs (fm->extend) > 2000) {
979         if (warn) {
980             mp_snprintf(s,128, 
981                 "invalid entry for `%s': too big value of ExtendFont (%g)",
982                  fm->tfm_name, fm->extend / 1000.0);
983                     mp_warn(mp,s);
984         }
985         a += 32;
986     }
987     if (fm->pid != -1 &&
988         !(is_truetype (fm) && is_included (fm) &&
989           is_subsetted (fm) && !is_reencoded (fm))) {
990         if (warn) {
991             mp_snprintf(s,128, 
992                 "invalid entry for `%s': "
993                  "PidEid can be used only with subsetted non-reencoded TrueType fonts",
994                  fm->tfm_name);
995                     mp_warn(mp,s);
996         }
997         a += 64;
998     }
999     return a;
1000 }
1001
1002 @ returns true if s is one of the 14 std. font names; speed-trimmed. 
1003
1004 @c static boolean check_basefont (char *s) {
1005     static const char *basefont_names[] = {
1006         "Courier",              /* 0:7 */
1007         "Courier-Bold",         /* 1:12 */
1008         "Courier-Oblique",      /* 2:15 */
1009         "Courier-BoldOblique",  /* 3:19 */
1010         "Helvetica",            /* 4:9 */
1011         "Helvetica-Bold",       /* 5:14 */
1012         "Helvetica-Oblique",    /* 6:17 */
1013         "Helvetica-BoldOblique",        /* 7:21 */
1014         "Symbol",               /* 8:6 */
1015         "Times-Roman",          /* 9:11 */
1016         "Times-Bold",           /* 10:10 */
1017         "Times-Italic",         /* 11:12 */
1018         "Times-BoldItalic",     /* 12:16 */
1019         "ZapfDingbats"          /* 13:12 */
1020     };
1021     static const int Index[] =
1022         { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1023         -1, 3, -1, 7
1024     };
1025     const size_t n = strlen (s);
1026     int k = -1;
1027     if (n > 21)
1028         return false;
1029     if (n == 12) {              /* three names have length 12 */
1030         switch (*s) {
1031         case 'C':
1032             k = 1;              /* Courier-Bold */
1033             break;
1034         case 'T':
1035             k = 11;             /* Times-Italic */
1036             break;
1037         case 'Z':
1038             k = 13;             /* ZapfDingbats */
1039             break;
1040         default:
1041             return false;
1042         }
1043     } else
1044         k = Index[n];
1045     if (k > -1 && !strcmp (basefont_names[k], s))
1046         return true;
1047     return false;
1048 }
1049
1050
1051 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1052
1053 @c static void fm_scan_line (MP mp) {
1054     int a, b, c, j, u = 0, v = 0;
1055     float d;
1056     fm_entry *fm;
1057     char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1058     char *p, *q, *r, *s;
1059     char warn_s[128];
1060     switch (mp->ps->mitem->type) {
1061     case MAPFILE:
1062         p = fm_line;
1063         do {
1064             c = fm_getchar (mp);
1065             append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1066         } while (c != 10);
1067         *(--p) = '\0';
1068         r = fm_line;
1069         break;
1070     case MAPLINE:
1071         r = mp->ps->mitem->map_line;
1072         break;
1073     default:
1074         assert (0);
1075     }
1076     if (*r == '\0' || is_cfg_comment (*r))
1077         return;
1078     fm = new_fm_entry (mp);
1079     read_field (r, q, buf);
1080     set_field (tfm_name);
1081     p = r;
1082     read_field (r, q, buf);
1083     if (*buf != '<' && *buf != '"')
1084         set_field (ps_name);
1085     else
1086         r = p;                  /* unget the field */
1087     if (isdigit (*r)) {         /* font flags given */
1088         fm->flags = atoi (r);
1089         while (isdigit (*r))
1090             r++;
1091     }
1092     while (1) {                 /* loop through "specials", encoding, font file */
1093         skip (r, ' ');
1094         switch (*r) {
1095         case '\0':
1096             goto DONE;
1097         case '"':              /* opening quote */
1098             r++;
1099             u = v = 0;
1100             do {
1101                 skip (r, ' ');
1102                 if (sscanf (r, "%f %n", &d, &j) > 0) {
1103                     s = r + j;  /* jump behind number, eat also blanks, if any */
1104                     if (*(s - 1) == 'E' || *(s - 1) == 'e')
1105                         s--;    /* e. g. 0.5ExtendFont: \%f = 0.5E */
1106                     if (str_prefix (s, "SlantFont")) {
1107                         d *= 1000.0;    /* correct rounding also for neg. numbers */
1108                         fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1109                         r = s + strlen ("SlantFont");
1110                     } else if (str_prefix (s, "ExtendFont")) {
1111                         d *= 1000.0;
1112                         fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1113                         if (fm->extend == 1000)
1114                             fm->extend = 0;
1115                         r = s + strlen ("ExtendFont");
1116                     } else {    /* unknown name */
1117                         for (r = s; 
1118                              *r != ' ' && *r != '"' && *r != '\0'; 
1119                              r++); /* jump over name */
1120                         c = *r; /* remember char for temporary end of string */
1121                         *r = '\0';
1122                         mp_snprintf(warn_s,128,
1123                             "invalid entry for `%s': unknown name `%s' ignored",
1124                              fm->tfm_name, s);
1125                         mp_warn(mp,warn_s);
1126                         *r = c;
1127                     }
1128                 } else
1129                     for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1130             }
1131             while (*r == ' ');
1132             if (*r == '"')      /* closing quote */
1133                 r++;
1134             else {
1135                 mp_snprintf(warn_s,128,
1136                     "invalid entry for `%s': closing quote missing",
1137                      fm->tfm_name);
1138                 mp_warn(mp,warn_s);
1139                 goto bad_line;
1140             }
1141             break;
1142         case 'P':              /* handle cases for subfonts like 'PidEid=3,1' */
1143             if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1144                 fm->pid = a;
1145                 fm->eid = b;
1146                 r += c;
1147                 break;
1148             }
1149         default:               /* encoding or font file specification */
1150             a = b = 0;
1151             if (*r == '<') {
1152                 a = *r++;
1153                 if (*r == '<' || *r == '[')
1154                     b = *r++;
1155             }
1156             read_field (r, q, buf);
1157             /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1158             if (strlen (buf) > 4 && strcasecmp (strend (buf) - 4, ".enc") == 0) {
1159                 fm->encoding = mp_add_enc (mp, buf);
1160                 u = v = 0;      /* u, v used if intervening blank: "<< foo" */
1161             } else if (strlen (buf) > 0) {      /* file name given */
1162                 /* font file, formats:
1163                  * subsetting:    '<cmr10.pfa'
1164                  * no subsetting: '<<cmr10.pfa'
1165                  * no embedding:  'cmr10.pfa'
1166                  */
1167                 if (a == '<' || u == '<') {
1168                   set_included (fm);
1169                   if ((a == '<' && b == 0) || (a == 0 && v == 0))
1170                     set_subsetted (fm);
1171                   /* otherwise b == '<' (or '[') => no subsetting */
1172                 }
1173                 set_field (ff_name);
1174                 u = v = 0;
1175             } else {
1176                 u = a;
1177                 v = b;
1178             }
1179         }
1180     }
1181   DONE:
1182     if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1183         set_basefont (fm);
1184     if (is_fontfile (fm)
1185         && strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1186         set_truetype (fm);
1187     if (check_fm_entry (mp,fm, true) != 0)
1188         goto bad_line;
1189     /*
1190        Until here the map line has been completely scanned without errors;
1191        fm points to a valid, freshly filled-out |fm_entry| structure.
1192        Now follows the actual work of registering/deleting.
1193      */
1194     if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0)    /* if success */
1195         return;
1196   bad_line:
1197     delete_fm_entry (fm);
1198 }
1199
1200
1201 @c static void fm_read_info (MP mp) {
1202     char *n;
1203     char s[256];
1204     if (mp->ps->tfm_tree == NULL)
1205         create_avl_trees (mp);
1206     if (mp->ps->mitem->map_line == NULL)    /* nothing to do */
1207         return;
1208     mp->ps->mitem->lineno = 1;
1209     switch (mp->ps->mitem->type) {
1210     case MAPFILE:
1211         n = mp->ps->mitem->map_line;
1212         mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1213         if (!mp->ps->fm_file) {
1214             mp_snprintf(s,256,"cannot open font map file %s",n);
1215             mp_warn(mp,s);
1216         } else {
1217             int save_selector = mp->selector;
1218             mp_normalize_selector(mp);
1219             mp_print (mp, "{");
1220             mp_print (mp, n);
1221             while (!fm_eof ()) {
1222                 fm_scan_line (mp);
1223                 mp->ps->mitem->lineno++;
1224             }
1225             fm_close ();
1226             mp_print (mp,"}");
1227             mp->selector = save_selector;
1228             mp->ps->fm_file = NULL;
1229         }
1230         mp_xfree(n);
1231         break;
1232     case MAPLINE:
1233         fm_scan_line (mp);
1234         break;
1235     default:
1236         assert (0);
1237     }
1238     mp->ps->mitem->map_line = NULL;         /* done with this line */
1239     return;
1240 }
1241
1242 @ @c static void init_fm (fm_entry * fm, font_number f) {
1243     if (fm->tfm_num == null_font ) {
1244         fm->tfm_num = f;
1245         fm->tfm_avail = TFM_FOUND;
1246     }
1247 }
1248
1249 @ @<Declarations@>=
1250 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1251
1252 @ @c 
1253 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1254     char *tfm;
1255     fm_entry *fm;
1256     fm_entry tmp;
1257     int e;
1258     if (mp->ps->tfm_tree == NULL)
1259         fm_read_info (mp);        /* only to read default map file */
1260     tfm = mp->font_name[f];
1261     assert (strcmp (tfm, nontfm));
1262     /* Look up for full <tfmname>[+-]<expand> */
1263     tmp.tfm_name = tfm;
1264     fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1265     if (fm != NULL) {
1266         init_fm (fm, f);
1267         return (fm_entry *) fm;
1268     }
1269     tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1270     if (tfm == NULL)            /* not an expanded font, nothing to do */
1271         return NULL;
1272
1273     tmp.tfm_name = tfm;
1274     fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1275     if (fm != NULL) {           /* found an entry with the base tfm name, e.g. cmr10 */
1276       return (fm_entry *) fm; /* font expansion uses the base font */
1277     }
1278     return NULL;
1279 }
1280
1281 @  Early check whether a font file exists. Used e. g. for replacing fonts
1282    of embedded PDF files: Without font file, the font within the embedded
1283    PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1284    may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1285    only once per font file name + expansion parameter. This might help
1286    keeping speed, if many PDF pages with same fonts are to be embedded.
1287
1288    The |ff_tree| contains only font files, which are actually needed,
1289    so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1290
1291 @c 
1292 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1293     ff_entry *ff;
1294     ff_entry tmp;
1295     void **aa;
1296
1297     assert (fm->ff_name != NULL);
1298     tmp.ff_name = fm->ff_name;
1299     ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1300     if (ff == NULL) {           /* not yet in database */
1301         ff = new_ff_entry (mp);
1302         ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1303         ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1304         aa = avl_probe (mp->ps->ff_tree, ff);
1305         assert (aa != NULL);
1306     }
1307     return ff;
1308 }
1309
1310 @ Process map file given by its name or map line contents. Items not
1311 beginning with [+-=] flush default map file, if it has not yet been
1312 read. Leading blanks and blanks immediately following [+-=] are ignored.
1313
1314
1315 @c static void mp_process_map_item (MP mp, char *s, int type) {
1316     char *p;
1317     int mode;
1318     if (*s == ' ')
1319         s++;                    /* ignore leading blank */
1320     switch (*s) {
1321     case '+':                  /* +mapfile.map, +mapline */
1322         mode = FM_DUPIGNORE;    /* insert entry, if it is not duplicate */
1323         s++;
1324         break;
1325     case '=':                  /* =mapfile.map, =mapline */
1326         mode = FM_REPLACE;      /* try to replace earlier entry */
1327         s++;
1328         break;
1329     case '-':                  /* -mapfile.map, -mapline */
1330         mode = FM_DELETE;       /* try to delete entry */
1331         s++;
1332         break;
1333     default:
1334         mode = FM_DUPIGNORE;    /* like +, but also: */
1335         mp_xfree(mp->ps->mitem->map_line);
1336         mp->ps->mitem->map_line = NULL;     /* flush default map file name */
1337     }
1338     if (*s == ' ')
1339         s++;                    /* ignore blank after [+-=] */
1340     p = s;                      /* map item starts here */
1341     switch (type) {
1342     case MAPFILE:              /* remove blank at end */
1343         while (*p != '\0' && *p != ' ')
1344             p++;
1345         *p = '\0';
1346         break;
1347     case MAPLINE:              /* blank at end allowed */
1348         break;
1349     default:
1350         assert (0);
1351     }
1352     if (mp->ps->mitem->map_line != NULL)    /* read default map file first */
1353         fm_read_info (mp);
1354     if (*s != '\0') {           /* only if real item to process */
1355         mp->ps->mitem->mode = mode;
1356         mp->ps->mitem->type = type;
1357         mp->ps->mitem->map_line = s;
1358         fm_read_info (mp);
1359     }
1360 }
1361
1362 @ @<Exported function headers@>=
1363 void mp_map_file (MP mp, str_number t);
1364 void mp_map_line (MP mp, str_number t);
1365 void mp_init_map_file (MP mp, int is_troff);
1366
1367 @ @c 
1368 void mp_map_file (MP mp, str_number t) {
1369   char *s = mp_xstrdup(mp,mp_str (mp,t));
1370   mp_process_map_item (mp, s, MAPFILE);
1371   mp_xfree (s);
1372 }
1373 void mp_map_line (MP mp, str_number t) {
1374   char *s = mp_xstrdup(mp,mp_str (mp,t));
1375   mp_process_map_item (mp, s, MAPLINE);
1376   mp_xfree (s);
1377 }
1378
1379
1380 @c void mp_init_map_file (MP mp, int is_troff) {
1381     char *r;
1382     mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1383     mp->ps->mitem->mode = FM_DUPIGNORE;
1384     mp->ps->mitem->type = MAPFILE;
1385     mp->ps->mitem->map_line = NULL;
1386     r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap);
1387     if (r != NULL) {
1388       mp_xfree(r);
1389       mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1390     } else {
1391       if (is_troff) {
1392              mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1393       } else {
1394              mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1395       }
1396     }
1397 }
1398
1399 @ @<Dealloc variables@>=
1400 if (mp->ps->mitem!=NULL) {
1401   mp_xfree(mp->ps->mitem->map_line);
1402   mp_xfree(mp->ps->mitem);
1403 }
1404
1405 @ cleaning up... 
1406
1407 @c
1408 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1409     fm_entry *fm;
1410     (void)pb;
1411     fm = (fm_entry *) pa;
1412     if (!has_pslink (fm))
1413         delete_fm_entry (fm);
1414     else
1415         unset_tfmlink (fm);
1416 }
1417 static void destroy_fm_entry_ps (void *pa, void *pb) {
1418     fm_entry *fm;
1419     (void)pb;
1420     fm = (fm_entry *) pa;
1421     if (!has_tfmlink (fm))
1422         delete_fm_entry (fm);
1423     else
1424         unset_pslink (fm);
1425 }
1426 static void destroy_ff_entry (void *pa, void *pb) {
1427     ff_entry *ff;
1428     (void)pb;
1429     ff = (ff_entry *) pa;
1430     delete_ff_entry (ff);
1431
1432
1433 @ @<Declarations@>=
1434 static void fm_free (MP mp);
1435
1436 @ @c
1437 static void fm_free (MP mp) {
1438     if (mp->ps->tfm_tree != NULL)
1439         avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1440     if (mp->ps->ps_tree != NULL)
1441         avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1442     if (mp->ps->ff_tree != NULL)
1443         avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1444 }
1445
1446 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1447 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1448 can be used when |internal[prologues]>0|.  Each line consists of a \TeX\ name,
1449 one or more spaces, a PostScript name, and possibly a space and some other junk.
1450 This routine reads the table, updates |font_ps_name| entries starting after
1451 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.  
1452
1453 @d ps_tab_name "psfonts.map"  /* locates font name translation table */
1454
1455 @<Declarations@>=
1456 static void mp_read_psname_table (MP mp) ;
1457
1458 @ @c 
1459 static void mp_read_psname_table (MP mp) {
1460   font_number k;
1461   if (mp->ps->mitem == NULL) {
1462     mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1463     mp->ps->mitem->mode = FM_DUPIGNORE;
1464     mp->ps->mitem->type = MAPFILE;
1465     mp->ps->mitem->map_line = NULL;
1466   }
1467   mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name); 
1468   fm_read_info (mp);
1469   for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1470     if (mp_has_fm_entry(mp, k, NULL)) {
1471       mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1472     }
1473   }
1474   mp->last_ps_fnum=mp->last_fnum;
1475 }
1476
1477
1478 @ The traditional function is a lot shorter now.
1479
1480
1481
1482 @* \[44c] Helper functions for Type1 fonts.
1483
1484 @<Types...@>=
1485 typedef char char_entry;
1486 typedef unsigned char  Byte;
1487 typedef Byte  Bytef;
1488
1489 @ @<Glob...@>=
1490 char_entry *char_ptr, *char_array;
1491 size_t char_limit;
1492 char *job_id_string;
1493
1494 @ @<Set initial...@>=
1495 mp->ps->char_array = NULL;
1496 mp->ps->job_id_string = NULL;
1497
1498
1499 @d SMALL_ARRAY_SIZE    256
1500 @d Z_NULL  0  
1501
1502 @c 
1503 void mp_set_job_id (MP mp) {
1504     char *name_string, *format_string, *s;
1505     size_t slen;
1506     if (mp->ps->job_id_string != NULL)
1507        return;
1508     if ( mp->job_name==NULL )
1509        mp->job_name = mp_xstrdup(mp,"mpout");
1510     name_string = mp_xstrdup (mp,mp->job_name);
1511     format_string = mp_xstrdup (mp,mp->mem_ident);
1512     slen = SMALL_BUF_SIZE +
1513         strlen (name_string) +
1514         strlen (format_string);
1515     s = mp_xmalloc (mp,slen, sizeof (char));
1516     sprintf (s,"%.4d/%.2d/%.2d %.2d:%.2d %s %s",
1517                (mp->internal[mp_year]>>16),
1518                (mp->internal[mp_month]>>16), 
1519                (mp->internal[mp_day]>>16), 
1520                (mp->internal[mp_time]>>16) / 60, 
1521                (mp->internal[mp_time]>>16) % 60,
1522                 name_string, format_string);
1523     mp->ps->job_id_string = mp_xstrdup (mp,s);
1524     mp_xfree (s);
1525     mp_xfree (name_string);
1526     mp_xfree (format_string);
1527 }
1528 static void fnstr_append (MP mp, const char *s) {
1529     size_t l = strlen (s) + 1;
1530     alloc_array (char, l, SMALL_ARRAY_SIZE);
1531     strcat (mp->ps->char_ptr, s);
1532     mp->ps->char_ptr = strend (mp->ps->char_ptr);
1533 }
1534
1535 @ @<Exported function headers@>=
1536 void mp_set_job_id (MP mp) ;
1537
1538 @ @<Dealloc variables@>=
1539 mp_xfree(mp->ps->job_id_string);
1540
1541 @ this is not really a true crc32, but it should be just enough to keep
1542   subsets prefixes somewhat disjunct
1543
1544 @c
1545 static unsigned long crc32 (int oldcrc, const Byte *buf, int len) {
1546   unsigned long ret = 0;
1547   int i;
1548   if (oldcrc==0)
1549         ret = (23<<24)+(45<<16)+(67<<8)+89;
1550   else 
1551       for (i=0;i<len;i++)
1552           ret = (ret<<2)+buf[i];
1553   return ret;
1554 }
1555 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1556   integer b; /* |char_base[f]| */
1557   b=mp->char_base[f];
1558   if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1559     return true;
1560   else
1561     return false;
1562 }
1563
1564 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, int tex_font)
1565 {
1566     char tag[7];
1567     unsigned long crc;
1568     int i;
1569     size_t l ;
1570     if (mp->ps->job_id_string ==NULL)
1571       mp_fatal_error(mp, "no job id!");
1572     l = strlen (mp->ps->job_id_string) + 1;
1573     
1574     alloc_array (char, l, SMALL_ARRAY_SIZE);
1575     strcpy (mp->ps->char_array, mp->ps->job_id_string);
1576     mp->ps->char_ptr = strend (mp->ps->char_array);
1577     if (fm_cur->tfm_name != NULL) {
1578         fnstr_append (mp," TFM name: ");
1579         fnstr_append (mp,fm_cur->tfm_name);
1580     }
1581     fnstr_append (mp," PS name: ");
1582     if (fm_cur->ps_name != NULL)
1583         fnstr_append (mp,fm_cur->ps_name);
1584     fnstr_append (mp," Encoding: ");
1585     if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1586         fnstr_append (mp,(fm_cur->encoding)->file_name);
1587     else
1588         fnstr_append (mp,"built-in");
1589     fnstr_append (mp," CharSet: ");
1590     for (i = 0; i < 256; i++)
1591         if (mp_char_marked (mp,tex_font, i) && glyph_names[i] != notdef) {
1592                         if (glyph_names[i]!=NULL) {
1593                           fnstr_append (mp,"/");
1594                           fnstr_append (mp,glyph_names[i]);
1595                         }
1596         }
1597     if (fm_cur->charset != NULL) {
1598         fnstr_append (mp," Extra CharSet: ");
1599         fnstr_append (mp, fm_cur->charset);
1600     }
1601     crc = crc32 (0L, Z_NULL, 0);
1602     crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1603     /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1604      * there are 26 uppercase chars ==> each char represents a number in range
1605      * |0..25|. The maximal number that can be represented by the tag is
1606      * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1607      * of the CRC must be dropped out.
1608      */
1609     for (i = 0; i < 6; i++) {
1610         tag[i] = 'A' + crc % 26;
1611         crc /= 26;
1612     }
1613     tag[6] = 0;
1614     fm_cur->subset_tag = mp_xstrdup (mp,tag);
1615 }
1616
1617
1618
1619
1620 @d external_enc()      (fm_cur->encoding)->glyph_names
1621 @d is_used_char(c)     mp_char_marked (mp, tex_font, c)
1622 @d end_last_eexec_line() 
1623     mp->ps->hexline_length = HEXLINE_WIDTH;
1624     end_hexline(mp); 
1625     mp->ps->t1_eexec_encrypt = false
1626 @d t1_log(s)           mp_print(mp,(char *)s)
1627 @d t1_putchar(c)       wps_chr(c)
1628 @d embed_all_glyphs(tex_font)  false
1629 @d t1_char(c)          c
1630 @d extra_charset()     mp->ps->dvips_extra_charset
1631 @d update_subset_tag()
1632 @d fixedcontent        true
1633
1634 @<Glob...@>=
1635 #define PRINTF_BUF_SIZE     1024
1636 char *dvips_extra_charset;
1637 char *cur_enc_name;
1638 unsigned char *grid;
1639 char *ext_glyph_names[256];
1640 char print_buf[PRINTF_BUF_SIZE];
1641 int t1_byte_waiting;
1642
1643 @ @<Set initial ...@>=
1644 mp->ps->dvips_extra_charset=NULL;
1645 mp->ps->t1_byte_waiting=0;
1646
1647 @
1648 @d t1_ungetchar(A) mp->ps->t1_byte_waiting=A
1649 @d t1_eof()        (mp->eof_file)(mp,mp->ps->t1_file)
1650 @d t1_close()      (mp->close_file)(mp,mp->ps->t1_file)
1651 @d valid_code(c)   (c >= 0 && c < 256)
1652
1653 @c
1654 int t1_getchar (MP mp) {
1655   size_t len = 1;
1656   unsigned char abyte=0;
1657   void *byte_ptr = &abyte;  
1658   if (mp->ps->t1_byte_waiting) {
1659     abyte = mp->ps->t1_byte_waiting;
1660     mp->ps->t1_byte_waiting = 0;
1661   } else {
1662     (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
1663   }
1664   return abyte;
1665 }
1666
1667 @ @<Static variables in the outer block@>=
1668 static const char *standard_glyph_names[256] =
1669     { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1670     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1671     notdef, notdef, notdef, notdef, notdef, notdef,
1672     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1673     "space", "exclam", "quotedbl", "numbersign",
1674     "dollar", "percent", "ampersand", "quoteright", "parenleft",
1675     "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1676     "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1677     "eight", "nine", "colon", "semicolon", "less",
1678     "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1679     "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1680     "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1681     "backslash", "bracketright", "asciicircum", "underscore",
1682     "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1683     "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1684     "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1685     notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1686     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1687     notdef, notdef, notdef, notdef, notdef, notdef,
1688     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1689     notdef, notdef, notdef, "exclamdown", "cent",
1690     "sterling", "fraction", "yen", "florin", "section", "currency",
1691     "quotesingle", "quotedblleft", "guillemotleft",
1692     "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1693     "dagger", "daggerdbl", "periodcentered", notdef,
1694     "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1695     "quotedblright", "guillemotright", "ellipsis", "perthousand",
1696     notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1697     "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1698     "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1699     notdef, notdef, notdef, notdef, notdef, notdef,
1700     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1701     notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1702     notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1703     notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1704     notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1705     "germandbls", notdef, notdef, notdef, notdef };
1706 static const char charstringname[] = "/CharStrings";
1707
1708 @ @<Glob...@>=
1709 char **t1_glyph_names;
1710 char *t1_builtin_glyph_names[256];
1711 char charsetstr[0x4000];
1712 boolean read_encoding_only;
1713 int t1_encoding;
1714
1715 @ @c
1716 #define T1_BUF_SIZE   0x10
1717
1718 #define CS_HSTEM            1
1719 #define CS_VSTEM            3
1720 #define CS_VMOVETO          4
1721 #define CS_RLINETO          5
1722 #define CS_HLINETO          6
1723 #define CS_VLINETO          7
1724 #define CS_RRCURVETO        8
1725 #define CS_CLOSEPATH        9
1726 #define CS_CALLSUBR         10
1727 #define CS_RETURN           11
1728 #define CS_ESCAPE           12
1729 #define CS_HSBW             13
1730 #define CS_ENDCHAR          14
1731 #define CS_RMOVETO          21
1732 #define CS_HMOVETO          22
1733 #define CS_VHCURVETO        30
1734 #define CS_HVCURVETO        31
1735 #define CS_1BYTE_MAX        (CS_HVCURVETO + 1)
1736
1737 #define CS_DOTSECTION       CS_1BYTE_MAX + 0
1738 #define CS_VSTEM3           CS_1BYTE_MAX + 1
1739 #define CS_HSTEM3           CS_1BYTE_MAX + 2
1740 #define CS_SEAC             CS_1BYTE_MAX + 6
1741 #define CS_SBW              CS_1BYTE_MAX + 7
1742 #define CS_DIV              CS_1BYTE_MAX + 12
1743 #define CS_CALLOTHERSUBR    CS_1BYTE_MAX + 16
1744 #define CS_POP              CS_1BYTE_MAX + 17
1745 #define CS_SETCURRENTPOINT  CS_1BYTE_MAX + 33
1746 #define CS_2BYTE_MAX        (CS_SETCURRENTPOINT + 1)
1747 #define CS_MAX              CS_2BYTE_MAX
1748
1749 @ @<Types...@>=
1750 typedef unsigned char byte;
1751 typedef struct {
1752     byte nargs;                 /* number of arguments */
1753     boolean bottom;             /* take arguments from bottom of stack? */
1754     boolean clear;              /* clear stack? */
1755     boolean valid;
1756 } cc_entry;                     /* CharString Command */
1757 typedef struct {
1758     char *glyph_name;                 /* glyph name (or notdef for Subrs entry) */
1759     byte *data;
1760     unsigned short len;         /* length of the whole string */
1761     unsigned short cslen;       /* length of the encoded part of the string */
1762     boolean is_used;
1763     boolean valid;
1764 } cs_entry;
1765
1766
1767 @d t1_c1 52845
1768 @d t1_c2 22719
1769
1770 @<Glob...@>=
1771 unsigned short t1_dr, t1_er;
1772 unsigned short t1_cslen;
1773 short t1_lenIV;
1774
1775 @ @<Types...@>=
1776 typedef char t1_line_entry;
1777 typedef char t1_buf_entry;
1778
1779 @ @<Glob...@>=
1780 t1_line_entry *t1_line_ptr, *t1_line_array;
1781 size_t t1_line_limit;
1782 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1783 size_t t1_buf_limit;
1784 int cs_start;
1785 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1786 char *cs_dict_start, *cs_dict_end;
1787 int cs_count, cs_size, cs_size_pos;
1788 cs_entry *subr_tab;
1789 char *subr_array_start, *subr_array_end;
1790 int subr_max, subr_size, subr_size_pos;
1791
1792 @ @<Set initial...@>=
1793 mp->ps->t1_line_array = NULL;
1794 mp->ps->t1_buf_array = NULL;
1795
1796
1797  This list contains the begin/end tokens commonly used in the 
1798  /Subrs array of a Type 1 font.                                
1799
1800 @<Static variables in the outer block@>=
1801 static const char *cs_token_pairs_list[][2] = {
1802     {" RD", "NP"},
1803     {" -|", "|"},
1804     {" RD", "noaccess put"},
1805     {" -|", "noaccess put"},
1806     {NULL, NULL}
1807 };
1808
1809 @ @<Glob...@>=
1810 const char **cs_token_pair;
1811 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1812 int t1_in_eexec;  /* 0 before eexec-encrypted, 1 during, 2 after */
1813 long t1_block_length;
1814 int last_hexbyte;
1815 void *t1_file;
1816 int hexline_length;
1817
1818
1819 @d HEXLINE_WIDTH 64
1820
1821 @<Set initial ...@>=
1822 mp->ps->hexline_length = 0;
1823
1824
1825 @d t1_prefix(s)        str_prefix(mp->ps->t1_line_array, s)
1826 @d t1_buf_prefix(s)    str_prefix(mp->ps->t1_buf_array, s)
1827 @d t1_suffix(s)        str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1828 @d t1_buf_suffix(s)    str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1829 @d t1_charstrings()    strstr(mp->ps->t1_line_array, charstringname)
1830 @d t1_subrs()          t1_prefix("/Subrs")
1831 @d t1_end_eexec()      t1_suffix("mark currentfile closefile")
1832 @d t1_cleartomark()    t1_prefix("cleartomark")
1833
1834 @d isdigit(A) ((A)>='0'&&(A)<='9')
1835
1836 @c
1837 static void end_hexline (MP mp) {
1838   if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
1839     wps_cr; 
1840     mp->ps->hexline_length = 0;
1841   }
1842 }
1843 static void t1_check_pfa (MP mp) {
1844     const int c = t1_getchar (mp);
1845     mp->ps->t1_pfa = (c != 128) ? true : false;
1846     t1_ungetchar (c);
1847 }
1848 static int t1_getbyte (MP mp)
1849 {
1850     int c = t1_getchar (mp);
1851     if (mp->ps->t1_pfa)
1852         return c;
1853     if (mp->ps->t1_block_length == 0) {
1854         if (c != 128)
1855          mp_fatal_error (mp, "invalid marker");
1856         c = t1_getchar (mp);
1857         if (c == 3) {
1858             while (!t1_eof ())
1859                 t1_getchar (mp);
1860             return EOF;
1861         }
1862         mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
1863         mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 8;
1864         mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 16;
1865         mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 24;
1866         c = t1_getchar (mp);
1867     }
1868     mp->ps->t1_block_length--;
1869     return c;
1870 }
1871 static int hexval (int c) {
1872     if (c >= 'A' && c <= 'F')
1873         return c - 'A' + 10;
1874     else if (c >= 'a' && c <= 'f')
1875         return c - 'a' + 10;
1876     else if (c >= '0' && c <= '9')
1877         return c - '0';
1878     else
1879         return -1;
1880 }
1881 static byte edecrypt (MP mp, byte cipher) {
1882     byte plain;
1883     if (mp->ps->t1_pfa) {
1884         while (cipher == 10 || cipher == 13)
1885             cipher = t1_getbyte (mp);
1886         mp->ps->last_hexbyte = cipher = (hexval (cipher) << 4) + hexval (t1_getbyte (mp));
1887     }
1888     plain = (cipher ^ (mp->ps->t1_dr >> 8));
1889     mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * t1_c1 + t1_c2;
1890     return plain;
1891 }
1892 static byte cdecrypt (byte cipher, unsigned short *cr)
1893 {
1894     const byte plain = (cipher ^ (*cr >> 8));
1895     *cr = (cipher + *cr) * t1_c1 + t1_c2;
1896     return plain;
1897 }
1898 static byte eencrypt (MP mp, byte plain)
1899 {
1900     const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1901     mp->ps->t1_er = (cipher + mp->ps->t1_er) * t1_c1 + t1_c2;
1902     return cipher;
1903 }
1904
1905 static byte cencrypt (byte plain, unsigned short *cr)
1906 {
1907     const byte cipher = (plain ^ (*cr >> 8));
1908     *cr = (cipher + *cr) * t1_c1 + t1_c2;
1909     return cipher;
1910 }
1911
1912 static char *eol (char *s) {
1913     char *p = strend (s);
1914     if (p - s > 1 && p[-1] != 10) {
1915         *p++ = 10;
1916         *p = 0;
1917     }
1918     return p;
1919 }
1920 static float t1_scan_num (MP mp, char *p, char **r)
1921 {
1922     float f;
1923     char s[128];
1924     skip (p, ' ');
1925     if (sscanf (p, "%g", &f) != 1) {
1926         remove_eol (p, mp->ps->t1_line_array); 
1927             mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1928         mp_fatal_error(mp,s);
1929     }
1930     if (r != NULL) {
1931         for (; isdigit (*p) || *p == '.' ||
1932              *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1933         *r = p;
1934     }
1935     return f;
1936 }
1937
1938 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1939                            const char *s)
1940 {
1941     const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1942     if (*s1 == 10)
1943         s1--;
1944     while (s1 >= begin_buf && s2 >= s) {
1945         if (*s1-- != *s2--)
1946             return false;
1947     }
1948     return s2 < s;
1949 }
1950
1951 @
1952
1953 @d alloc_array(T, n, s) do {
1954     if (mp->ps->T##_array == NULL) {
1955         mp->ps->T##_limit = (s);
1956         if ((unsigned)(n) > mp->ps->T##_limit)
1957             mp->ps->T##_limit = (n);
1958         mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
1959         mp->ps->T##_ptr = mp->ps->T##_array;
1960     }
1961     else if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
1962         size_t last_ptr_index;
1963         last_ptr_index = mp->ps->T##_ptr - mp->ps->T##_array;
1964         mp->ps->T##_limit *= 2;
1965         if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
1966             mp->ps->T##_limit = mp->ps->T##_ptr - mp->ps->T##_array + (n);
1967         mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
1968         mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
1969     }
1970 } while (0)
1971
1972 @d out_eexec_char(A)      t1_outhex(mp,(A))
1973  
1974 @c
1975 static void t1_outhex (MP mp, byte b)
1976 {
1977     static const char *hexdigits = "0123456789ABCDEF";
1978     t1_putchar (hexdigits[b / 16]);
1979     t1_putchar (hexdigits[b % 16]);
1980     mp->ps->hexline_length += 2;
1981     end_hexline (mp);
1982 }
1983 static void t1_getline (MP mp) {
1984     int c, l, eexec_scan;
1985     char *p;
1986     static const char eexec_str[] = "currentfile eexec";
1987     static int eexec_len = 17;  /* |strlen(eexec_str)| */
1988   RESTART:
1989     if (t1_eof ())
1990         mp_fatal_error (mp,"unexpected end of file");
1991     mp->ps->t1_line_ptr = mp->ps->t1_line_array;
1992     alloc_array (t1_line, 1, T1_BUF_SIZE);
1993     mp->ps->t1_cslen = 0;
1994     eexec_scan = 0;
1995     c = t1_getbyte (mp);
1996     if (c == EOF)
1997         goto EXIT;
1998     while (!t1_eof ()) {
1999         if (mp->ps->t1_in_eexec == 1)
2000             c = edecrypt (mp,c);
2001         alloc_array (t1_line, 1, T1_BUF_SIZE);
2002         append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2003         if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
2004             if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
2005                 eexec_scan++;
2006             else
2007                 eexec_scan = -1;
2008         }
2009         if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2010             break;
2011         if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 && 
2012             (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2013             (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2014             p = mp->ps->t1_line_ptr - 5;
2015             while (*p != ' ')
2016                 p--;
2017             mp->ps->t1_cslen = l = t1_scan_num (mp, p + 1, 0);
2018             mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;     
2019                   /* |mp->ps->cs_start| is an index now */
2020             alloc_array (t1_line, l, T1_BUF_SIZE);
2021             while (l-- > 0)
2022                 *mp->ps->t1_line_ptr++ = edecrypt (mp,t1_getbyte (mp));
2023         }
2024         c = t1_getbyte (mp);
2025     }
2026     alloc_array (t1_line, 2, T1_BUF_SIZE);      /* |append_eol| can append 2 chars */
2027     append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2028     if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2029         goto RESTART;
2030     if (eexec_scan == eexec_len)
2031         mp->ps->t1_in_eexec = 1;
2032   EXIT:
2033     /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2034     mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2035     alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2036 }
2037
2038 static void t1_putline (MP mp)
2039 {
2040     char *p = mp->ps->t1_line_array;
2041     if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2042         return;
2043     if (mp->ps->t1_eexec_encrypt) {
2044         while (p < mp->ps->t1_line_ptr)
2045             out_eexec_char (eencrypt (mp,*p++));
2046     } else {
2047         while (p < mp->ps->t1_line_ptr)
2048             t1_putchar (*p++);
2049         }
2050 }
2051
2052 static void t1_puts (MP mp, const char *s)
2053 {
2054     if (s != mp->ps->t1_line_array)
2055         strcpy (mp->ps->t1_line_array, s);
2056     mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2057     t1_putline (mp);
2058 }
2059
2060 static void t1_printf (MP mp, const char *fmt, ...)
2061 {
2062     va_list args;
2063     va_start (args, fmt);
2064     vsprintf (mp->ps->t1_line_array, fmt, args);
2065     t1_puts (mp,mp->ps->t1_line_array);
2066     va_end (args);
2067 }
2068
2069 static void t1_init_params (MP mp, const char *open_name_prefix,
2070                            char *cur_file_name) {
2071   if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2072     t1_log (open_name_prefix);
2073     t1_log (cur_file_name);
2074   }
2075     mp->ps->t1_lenIV = 4;
2076     mp->ps->t1_dr = 55665;
2077     mp->ps->t1_er = 55665;
2078     mp->ps->t1_in_eexec = 0;
2079     mp->ps->t1_cs = false;
2080     mp->ps->t1_scan = true;
2081     mp->ps->t1_synthetic = false;
2082     mp->ps->t1_eexec_encrypt = false;
2083     mp->ps->t1_block_length = 0;
2084     t1_check_pfa (mp);
2085 }
2086 static void  t1_close_font_file (MP mp, const char *close_name_suffix) {
2087   if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2088     t1_log (close_name_suffix);
2089   }
2090   t1_close ();
2091 }
2092
2093 static void  t1_check_block_len (MP mp, boolean decrypt) {
2094     int l, c;
2095     char s[128];
2096     if (mp->ps->t1_block_length == 0)
2097         return;
2098     c = t1_getbyte (mp);
2099     if (decrypt)
2100         c = edecrypt (mp,c);
2101     l = mp->ps->t1_block_length;
2102     if (!(l == 0 && (c == 10 || c == 13))) {
2103         mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2104         mp_warn(mp,s);
2105         while (l-- > 0)
2106           t1_getbyte (mp);
2107     }
2108 }
2109 static void  t1_start_eexec (MP mp, fm_entry *fm_cur) {
2110     int i;
2111     if (!mp->ps->t1_pfa)
2112      t1_check_block_len (mp, false);
2113     for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2114       edecrypt (mp, t1_getbyte (mp));
2115       *mp->ps->t1_line_ptr++ = 0;
2116     }
2117     mp->ps->t1_eexec_encrypt = true;
2118         if (!mp->ps->read_encoding_only)
2119           if (is_included (fm_cur))
2120         t1_putline (mp);          /* to put the first four bytes */
2121 }
2122 static void  t1_stop_eexec (MP mp) {
2123     int c;
2124     end_last_eexec_line ();
2125     if (!mp->ps->t1_pfa)
2126       t1_check_block_len (mp,true);
2127     else {
2128         c = edecrypt (mp, t1_getbyte (mp));
2129         if (!(c == 10 || c == 13)) {
2130            if (mp->ps->last_hexbyte == 0)
2131               t1_puts (mp,"00");
2132            else
2133               mp_warn (mp,"unexpected data after eexec");
2134         }
2135     }
2136     mp->ps->t1_cs = false;
2137     mp->ps->t1_in_eexec = 2;
2138 }
2139 static void  t1_modify_fm (MP mp) {
2140   mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2141 }
2142
2143 static void  t1_modify_italic (MP mp) {
2144   mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2145 }
2146
2147 @ @<Types...@>=
2148 typedef struct {
2149     const char *pdfname;
2150     const char *t1name;
2151     float value;
2152     boolean valid;
2153 } key_entry;
2154
2155 @
2156 @d FONT_KEYS_NUM  11
2157
2158 @<Declarations@>=
2159 static key_entry font_keys[FONT_KEYS_NUM] = {
2160     {"Ascent", "Ascender", 0, false},
2161     {"CapHeight", "CapHeight", 0, false},
2162     {"Descent", "Descender", 0, false},
2163     {"FontName", "FontName", 0, false},
2164     {"ItalicAngle", "ItalicAngle", 0, false},
2165     {"StemV", "StdVW", 0, false},
2166     {"XHeight", "XHeight", 0, false},
2167     {"FontBBox", "FontBBox", 0, false},
2168     {"", "", 0, false},
2169     {"", "", 0, false},
2170     {"", "", 0, false}
2171 };
2172
2173
2174
2175 @d ASCENT_CODE         0
2176 @d CAPHEIGHT_CODE      1
2177 @d DESCENT_CODE        2
2178 @d FONTNAME_CODE       3
2179 @d ITALIC_ANGLE_CODE   4
2180 @d STEMV_CODE          5
2181 @d XHEIGHT_CODE        6
2182 @d FONTBBOX1_CODE      7
2183 @d FONTBBOX2_CODE      8
2184 @d FONTBBOX3_CODE      9
2185 @d FONTBBOX4_CODE      10
2186 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2187
2188 @c
2189 static void  t1_scan_keys (MP mp, int tex_font,fm_entry *fm_cur) {
2190     int i, k;
2191     char *p, *r;
2192     key_entry *key;
2193     if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2194         if (t1_prefix ("/FontMatrix")) {
2195             t1_modify_fm (mp);
2196             return;
2197         }
2198         if (t1_prefix ("/ItalicAngle")) {
2199             t1_modify_italic (mp);
2200             return;
2201         }
2202     }
2203     if (t1_prefix ("/FontType")) {
2204         p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2205         if ((i = t1_scan_num (mp,p, 0)) != 1) {
2206             char s[128];
2207             mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2208             mp_fatal_error(mp,s);
2209         }
2210         return;
2211     }
2212     for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2213         if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2214             break;
2215     if (key - font_keys == MAX_KEY_CODE)
2216         return;
2217     key->valid = true;
2218     p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2219     skip (p, ' ');
2220     if ((k = key - font_keys) == FONTNAME_CODE) {
2221         if (*p != '/') {
2222           char s[128];
2223           remove_eol (p, mp->ps->t1_line_array);
2224           mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2225           mp_fatal_error(mp,s);
2226         }
2227         r = ++p;                /* skip the slash */
2228         if (is_included (fm_cur)) {
2229           /* save the fontname */
2230           strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2231           for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2232           mp->ps->fontname_buf[i]=0;
2233           
2234           if(is_subsetted (fm_cur)) {
2235             if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2236               make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2237             else
2238               make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2239
2240             alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1), 
2241                          T1_BUF_SIZE);
2242             strncpy (r, fm_cur->subset_tag , 6);
2243             *(r+6) = '-';
2244             strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2245             mp->ps->t1_line_ptr = eol (r);
2246           } else {
2247             /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2248             /*|*q = 0;|*/
2249             mp->ps->t1_line_ptr = eol (r);
2250           }
2251         }
2252         return;
2253     }
2254     if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2255         && (*p == '[' || *p == '{'))
2256         p++;
2257     if (k == FONTBBOX1_CODE) {
2258         for (i = 0; i < 4; i++) {
2259             key[i].value = t1_scan_num (mp, p, &r);
2260             p = r;
2261         }
2262         return;
2263     }
2264     key->value = t1_scan_num (mp, p, 0);
2265 }
2266 static void  t1_scan_param (MP mp, int tex_font,fm_entry *fm_cur)
2267 {
2268     static const char *lenIV = "/lenIV";
2269     if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2270         return;
2271     if (t1_prefix (lenIV)) {
2272         mp->ps->t1_lenIV = t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2273         return;
2274     }
2275     t1_scan_keys (mp, tex_font,fm_cur);
2276 }
2277 static void  copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2278     if (glyph_names[b] != notdef) {
2279         mp_xfree (glyph_names[b]);
2280         glyph_names[b] = (char *) notdef;
2281     }
2282     if (glyph_names[a] != notdef) {
2283         glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2284     }
2285 }
2286 static void  t1_builtin_enc (MP mp) {
2287     int i, a, b, c, counter = 0;
2288     char *r, *p;
2289     /*
2290      * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2291      */
2292     if (t1_suffix ("def")) {    /* predefined encoding */
2293         sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2294         if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2295             for (i = 0; i < 256; i++)
2296                 if (standard_glyph_names[i] == notdef)
2297                     mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2298                 else
2299                     mp->ps->t1_builtin_glyph_names[i] =
2300                         mp_xstrdup (mp,standard_glyph_names[i]);
2301             mp->ps->t1_encoding = ENC_STANDARD;
2302         } else {
2303             char s[128];
2304             mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2305                         mp->ps->t1_buf_array);
2306             mp_fatal_error(mp,s);
2307         }
2308         return;
2309     } else
2310         mp->ps->t1_encoding = ENC_BUILTIN;
2311     /*
2312      * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2313      * not a predefined encoding
2314      *
2315      * We have two possible forms of Encoding vector. The first case is
2316      *
2317      *     /Encoding [/a /b /c...] readonly def
2318      *
2319      * and the second case can look like
2320      *
2321      *     /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2322      *     dup 0 /x put
2323      *     dup 1 /y put
2324      *     ...
2325      *     readonly def
2326      */
2327     for (i = 0; i < 256; i++)
2328         mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2329     if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) {        /* the first case */
2330         r = strchr (mp->ps->t1_line_array, '[') + 1;
2331         skip (r, ' ');
2332         for (;;) {
2333             while (*r == '/') {
2334                 for (p = mp->ps->t1_buf_array, r++;
2335                      *r != 32 && *r != 10 && *r != ']' && *r != '/';
2336                      *p++ = *r++);
2337                 *p = 0;
2338                 skip (r, ' ');
2339                 if (counter > 255) {
2340                    mp_fatal_error
2341                         (mp, "encoding vector contains more than 256 names");
2342                 }
2343                 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2344                   mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2345                 counter++;
2346             }
2347             if (*r != 10 && *r != '%') {
2348                 if (str_prefix (r, "] def")
2349                     || str_prefix (r, "] readonly def"))
2350                     break;
2351                 else {
2352                     char s[128];
2353                     remove_eol (r, mp->ps->t1_line_array);
2354                     mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2355                                     mp->ps->t1_line_array);
2356                     mp_fatal_error(mp,s);
2357                 }
2358             }
2359             t1_getline (mp);
2360             r = mp->ps->t1_line_array;
2361         }
2362     } else {                    /* the second case */
2363         p = strchr (mp->ps->t1_line_array, 10);
2364         for (;;) {
2365             if (*p == 10) {
2366                 t1_getline (mp);
2367                 p = mp->ps->t1_line_array;
2368             }
2369             /*
2370                check for `dup <index> <glyph> put'
2371              */
2372             if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2373                 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2374                 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2375                     mp->ps->t1_builtin_glyph_names[i] = 
2376                       mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2377                 p = strstr (p, " put") + strlen (" put");
2378                 skip (p, ' ');
2379             }
2380             /*
2381                check for `dup dup <to> exch <from> get put'
2382              */
2383             else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2384                      && valid_code (a) && valid_code (b)) {
2385                 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2386                 p = strstr (p, " get put") + strlen (" get put");
2387                 skip (p, ' ');
2388             }
2389             /*
2390                check for `dup dup <from> <size> getinterval <to> exch putinterval'
2391              */
2392             else if (sscanf
2393                      (p, "dup dup %i %i getinterval %i exch putinterval",
2394                       &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2395                      && valid_code (c)) {
2396                 for (i = 0; i < c; i++)
2397                     copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2398                 p = strstr (p, " putinterval") + strlen (" putinterval");
2399                 skip (p, ' ');
2400             }
2401             /*
2402                check for `def' or `readonly def'
2403              */
2404             else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2405                      && strcmp (p, "def\n") == 0)
2406                 return;
2407             /*
2408                skip an unrecognizable word
2409              */
2410             else {
2411                 while (*p != ' ' && *p != 10)
2412                     p++;
2413                 skip (p, ' ');
2414             }
2415         }
2416     }
2417 }
2418
2419 static void  t1_check_end (MP mp) {
2420     if (t1_eof ())
2421         return;
2422     t1_getline (mp);
2423     if (t1_prefix ("{restore}"))
2424         t1_putline (mp);
2425 }
2426
2427 @ @<Types...@>=
2428 typedef struct {
2429     char *ff_name;              /* base name of font file */
2430     char *ff_path;              /* full path to font file */
2431 } ff_entry;
2432
2433 @ @c
2434 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2435     ff_entry *ff;
2436     ff = check_ff_exist (mp, fm_cur);
2437     if (ff->ff_path != NULL) {
2438         mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font);
2439     } else {
2440         mp_warn (mp, "cannot open Type 1 font file for reading");
2441         return false;
2442     }
2443     t1_init_params (mp,open_name_prefix,fm_cur->ff_name);
2444     mp->ps->fontfile_found = true;
2445     return true;
2446 }
2447
2448 static void  t1_scan_only (MP mp, int tex_font, fm_entry *fm_cur) {
2449     do {
2450         t1_getline (mp);
2451         t1_scan_param (mp,tex_font, fm_cur);
2452     }
2453     while (mp->ps->t1_in_eexec == 0);
2454     t1_start_eexec (mp,fm_cur);
2455     do {
2456         t1_getline (mp);
2457         t1_scan_param (mp,tex_font, fm_cur);
2458     }
2459     while (!(t1_charstrings () || t1_subrs ()));
2460 }
2461
2462 static void  t1_include (MP mp, int tex_font, fm_entry *fm_cur) {
2463     do {
2464         t1_getline (mp);
2465         t1_scan_param (mp,tex_font, fm_cur);
2466         t1_putline (mp);
2467     }
2468     while (mp->ps->t1_in_eexec == 0);
2469     t1_start_eexec (mp,fm_cur);
2470     do {
2471         t1_getline (mp);
2472         t1_scan_param (mp,tex_font, fm_cur);
2473         t1_putline (mp);
2474     }
2475     while (!(t1_charstrings () || t1_subrs ()));
2476     mp->ps->t1_cs = true;
2477     do {
2478         t1_getline (mp);
2479         t1_putline (mp);
2480     }
2481     while (!t1_end_eexec ());
2482     t1_stop_eexec (mp);
2483     if (fixedcontent) {         /* copy 512 zeros (not needed for PDF) */
2484         do {
2485             t1_getline (mp);
2486             t1_putline (mp);
2487         }
2488         while (!t1_cleartomark ());
2489         t1_check_end (mp);        /* write "{restore}if" if found */
2490     }
2491 }
2492
2493 @
2494 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2495         char s[128];
2496         mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2497         mp_fatal_error(mp,s);
2498   }
2499
2500 @c
2501 static const char **check_cs_token_pair (MP mp) {
2502     const char **p = (const char **) cs_token_pairs_list;
2503     for (; p[0] != NULL; ++p)
2504         if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2505             return p;
2506     return NULL;
2507 }
2508
2509 static void cs_store (MP mp, boolean is_subr) {
2510     char *p;
2511     cs_entry *ptr;
2512     int subr;
2513     for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2514          *mp->ps->t1_buf_ptr++ = *p++);
2515     *mp->ps->t1_buf_ptr = 0;
2516     if (is_subr) {
2517         subr = t1_scan_num (mp, p + 1, 0);
2518         check_subr (subr);
2519         ptr = mp->ps->subr_tab + subr;
2520     } else {
2521         ptr = mp->ps->cs_ptr++;
2522         if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2523           char s[128];
2524           mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2525           mp_fatal_error(mp,s);
2526         }
2527         if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0)     /* skip the slash */
2528             ptr->glyph_name = (char *) notdef;
2529         else
2530             ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2531     }
2532     /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2533     memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2534             (unsigned) (mp->ps->t1_cslen + 4));
2535     /* copy the end of cs data to |mp->ps->t1_buf_array| */
2536     for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2537          mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2538     *mp->ps->t1_buf_ptr++ = 10;
2539     if (is_subr && mp->ps->cs_token_pair == NULL)
2540         mp->ps->cs_token_pair = check_cs_token_pair (mp);
2541     ptr->len = mp->ps->t1_buf_ptr - mp->ps->t1_buf_array;
2542     ptr->cslen = mp->ps->t1_cslen;
2543     ptr->data = mp_xmalloc (mp,ptr->len , sizeof (byte));
2544     memcpy (ptr->data, mp->ps->t1_buf_array, ptr->len);
2545     ptr->valid = true;
2546 }
2547
2548 #define store_subr(mp)    cs_store(mp,true)
2549 #define store_cs(mp)      cs_store(mp,false)
2550
2551 #define CC_STACK_SIZE       24
2552
2553 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2554 static cc_entry cc_tab[CS_MAX];
2555 static boolean is_cc_init = false;
2556
2557
2558 #define cc_pop(N)                       \
2559     if (stack_ptr - cc_stack < (N))     \
2560         stack_error(N);                 \
2561     stack_ptr -= N
2562
2563 #define stack_error(N) {                \
2564     char s[256];                        \
2565     mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2566                  (int) N, (int)(stack_ptr - cc_stack));                  \
2567     mp_warn(mp,s);                    \
2568     goto cs_error;                    \
2569 }
2570
2571
2572 #define cc_get(N)   ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2573
2574 #define cc_push(V)  *stack_ptr++ = V
2575 #define cc_clear()  stack_ptr = cc_stack
2576
2577 #define set_cc(N, B, A, C) \
2578     cc_tab[N].nargs = A;   \
2579     cc_tab[N].bottom = B;  \
2580     cc_tab[N].clear = C;   \
2581     cc_tab[N].valid = true
2582
2583 static void cc_init (void) {
2584     int i;
2585     if (is_cc_init)
2586         return;
2587     for (i = 0; i < CS_MAX; i++)
2588         cc_tab[i].valid = false;
2589     set_cc (CS_HSTEM, true, 2, true);
2590     set_cc (CS_VSTEM, true, 2, true);
2591     set_cc (CS_VMOVETO, true, 1, true);
2592     set_cc (CS_RLINETO, true, 2, true);
2593     set_cc (CS_HLINETO, true, 1, true);
2594     set_cc (CS_VLINETO, true, 1, true);
2595     set_cc (CS_RRCURVETO, true, 6, true);
2596     set_cc (CS_CLOSEPATH, false, 0, true);
2597     set_cc (CS_CALLSUBR, false, 1, false);
2598     set_cc (CS_RETURN, false, 0, false);
2599     /*
2600        |set_cc(CS_ESCAPE,          false,  0, false);|
2601      */
2602     set_cc (CS_HSBW, true, 2, true);
2603     set_cc (CS_ENDCHAR, false, 0, true);
2604     set_cc (CS_RMOVETO, true, 2, true);
2605     set_cc (CS_HMOVETO, true, 1, true);
2606     set_cc (CS_VHCURVETO, true, 4, true);
2607     set_cc (CS_HVCURVETO, true, 4, true);
2608     set_cc (CS_DOTSECTION, false, 0, true);
2609     set_cc (CS_VSTEM3, true, 6, true);
2610     set_cc (CS_HSTEM3, true, 6, true);
2611     set_cc (CS_SEAC, true, 5, true);
2612     set_cc (CS_SBW, true, 4, true);
2613     set_cc (CS_DIV, false, 2, false);
2614     set_cc (CS_CALLOTHERSUBR, false, 0, false);
2615     set_cc (CS_POP, false, 0, false);
2616     set_cc (CS_SETCURRENTPOINT, true, 2, true);
2617     is_cc_init = true;
2618 }
2619
2620 @
2621
2622 @d cs_getchar(mp)    cdecrypt(*data++, &cr)
2623
2624 @d mark_subr(mp,n)    cs_mark(mp,0, n)
2625 @d mark_cs(mp,s)      cs_mark(mp,s, 0)
2626 @d SMALL_BUF_SIZE      256
2627
2628 @c
2629 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2630     char buf[SMALL_BUF_SIZE];
2631     char s[300];
2632     va_list args;
2633     va_start (args, fmt);
2634     vsprintf (buf, fmt, args);
2635     va_end (args);
2636     if (cs_name == NULL) {
2637         mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2638     } else {
2639        mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2640     }
2641     mp_warn(mp,s);
2642 }
2643
2644 static void cs_mark (MP mp, const char *cs_name, int subr)
2645 {
2646     byte *data;
2647     int i, b, cs_len;
2648     integer a, a1, a2;
2649     unsigned short cr;
2650     static integer lastargOtherSubr3 = 3;       /* the argument of last call to
2651                                                    OtherSubrs[3] */
2652     cs_entry *ptr;
2653     cc_entry *cc;
2654     if (cs_name == NULL) {
2655         check_subr (subr);
2656         ptr = mp->ps->subr_tab + subr;
2657         if (!ptr->valid)
2658           return;
2659     } else {
2660         if (mp->ps->cs_notdef != NULL &&
2661             (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2662             ptr = mp->ps->cs_notdef;
2663         else {
2664             for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2665                 if (strcmp (ptr->glyph_name, cs_name) == 0)
2666                     break;
2667             if (ptr == mp->ps->cs_ptr) {
2668                 char s[128];
2669                 mp_snprintf (s,128,"glyph `%s' undefined", cs_name);
2670                 mp_warn(mp,s);
2671                 return;
2672             }
2673             if (ptr->glyph_name == notdef)
2674                 mp->ps->cs_notdef = ptr;
2675         }
2676     }
2677     /* only marked CharString entries and invalid entries can be skipped;
2678        valid marked subrs must be parsed to keep the stack in sync */
2679     if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2680         return;
2681     ptr->is_used = true;
2682     cr = 4330;
2683     cs_len = ptr->cslen;
2684     data = ptr->data + 4;
2685     for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2686         cs_getchar (mp);
2687     while (cs_len > 0) {
2688         --cs_len;
2689         b = cs_getchar (mp);
2690         if (b >= 32) {
2691             if (b <= 246)
2692                 a = b - 139;
2693             else if (b <= 250) {
2694                 --cs_len;
2695                 a = ((b - 247) << 8) + 108 + cs_getchar (mp);
2696             } else if (b <= 254) {
2697                 --cs_len;
2698                 a = -((b - 251) << 8) - 108 - cs_getchar (mp);
2699             } else {
2700                 cs_len -= 4;
2701                 a = (cs_getchar (mp) & 0xff) << 24;
2702                 a |= (cs_getchar (mp) & 0xff) << 16;
2703                 a |= (cs_getchar (mp) & 0xff) << 8;
2704                 a |= (cs_getchar (mp) & 0xff) << 0;
2705                 if (sizeof (integer) > 4 && (a & 0x80000000))
2706                     a |= ~0x7FFFFFFF;
2707             }
2708             cc_push (a);
2709         } else {
2710             if (b == CS_ESCAPE) {
2711                 b = cs_getchar (mp) + CS_1BYTE_MAX;
2712                 cs_len--;
2713             }
2714             if (b >= CS_MAX) {
2715                 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2716                          (int) b);
2717                 goto cs_error;
2718             }
2719             cc = cc_tab + b;
2720             if (!cc->valid) {
2721                 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2722                 goto cs_error;
2723             }
2724             if (cc->bottom) {
2725                 if (stack_ptr - cc_stack < cc->nargs)
2726                     cs_warn (mp,cs_name, subr,
2727                              "less arguments on stack (%i) than required (%i)",
2728                              (int) (stack_ptr - cc_stack), (int) cc->nargs);
2729                 else if (stack_ptr - cc_stack > cc->nargs)
2730                     cs_warn (mp,cs_name, subr,
2731                              "more arguments on stack (%i) than required (%i)",
2732                              (int) (stack_ptr - cc_stack), (int) cc->nargs);
2733             }
2734             switch (cc - cc_tab) {
2735             case CS_CALLSUBR:
2736                 a1 = cc_get (-1);
2737                 cc_pop (1);
2738                 mark_subr (mp,a1);
2739                 if (!mp->ps->subr_tab[a1].valid) {
2740                     cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2741                     goto cs_error;
2742                 }
2743                 break;
2744             case CS_DIV:
2745                 cc_pop (2);
2746                 cc_push (0);
2747                 break;
2748             case CS_CALLOTHERSUBR:
2749                 if (cc_get (-1) == 3)
2750                     lastargOtherSubr3 = cc_get (-3);
2751                 a1 = cc_get (-2) + 2;
2752                 cc_pop (a1);
2753                 break;
2754             case CS_POP:
2755                 cc_push (lastargOtherSubr3);
2756                 /* the only case when we care about the value being pushed onto
2757                    stack is when POP follows CALLOTHERSUBR (changing hints by
2758                    OtherSubrs[3])
2759                  */
2760                 break;
2761             case CS_SEAC:
2762                 a1 = cc_get (3);
2763                 a2 = cc_get (4);
2764                 cc_clear ();
2765                 mark_cs (mp,standard_glyph_names[a1]);
2766                 mark_cs (mp,standard_glyph_names[a2]);
2767                 break;
2768             default:
2769                 if (cc->clear)
2770                     cc_clear ();
2771             }
2772         }
2773     }
2774     return;
2775   cs_error:                    /* an error occured during parsing */
2776     cc_clear ();
2777     ptr->valid = false;
2778     ptr->is_used = false;
2779 }
2780
2781 static void t1_subset_ascii_part (MP mp, int tex_font, fm_entry *fm_cur)
2782 {
2783     int i, j;
2784     t1_getline (mp);
2785     while (!t1_prefix ("/Encoding")) {
2786           t1_scan_param (mp,tex_font, fm_cur);
2787         t1_putline (mp);
2788         t1_getline (mp);
2789     }
2790     t1_builtin_enc (mp);
2791     if (is_reencoded (fm_cur))
2792         mp->ps->t1_glyph_names = external_enc ();
2793     else
2794         mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2795         /* 
2796     |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2797             make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2798         update_subset_tag ();
2799     }|
2800     */
2801     if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2802         t1_puts (mp,"/Encoding StandardEncoding def\n");
2803     else {
2804         t1_puts
2805             (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2806         for (i = 0, j = 0; i < 256; i++) {
2807             if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2808                 j++;
2809                 t1_printf (mp,"dup %i /%s put\n", (int) t1_char (i),
2810                            mp->ps->t1_glyph_names[i]);
2811             }
2812         }
2813         /* We didn't mark anything for the Encoding array. */
2814         /* We add "dup 0 /.notdef put" for compatibility   */
2815         /* with Acrobat 5.0.                               */
2816         if (j == 0)
2817             t1_puts (mp,"dup 0 /.notdef put\n");
2818         t1_puts (mp,"readonly def\n");
2819     }
2820     do {
2821         t1_getline (mp);
2822         t1_scan_param (mp,tex_font, fm_cur);
2823         if (!t1_prefix ("/UniqueID"))   /* ignore UniqueID for subsetted fonts */
2824             t1_putline (mp);
2825     }
2826     while (mp->ps->t1_in_eexec == 0);
2827 }
2828
2829 #define t1_subr_flush(mp)  t1_flush_cs(mp,true)
2830 #define t1_cs_flush(mp)    t1_flush_cs(mp,false)
2831
2832 static void cs_init (MP mp) {
2833     mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2834     mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2835     mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2836     mp->ps->cs_token_pair = NULL;
2837     mp->ps->subr_tab = NULL;
2838     mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2839     mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2840 }
2841
2842 static void init_cs_entry ( cs_entry * cs) {
2843     cs->data = NULL;
2844     cs->glyph_name = NULL;
2845     cs->len = 0;
2846     cs->cslen = 0;
2847     cs->is_used = false;
2848     cs->valid = false;
2849 }
2850
2851 static void t1_mark_glyphs (MP mp, int tex_font);
2852
2853 static void t1_read_subrs (MP mp, int tex_font, fm_entry *fm_cur)
2854 {
2855     int i, s;
2856     cs_entry *ptr;
2857     t1_getline (mp);
2858     while (!(t1_charstrings () || t1_subrs ())) {
2859         t1_scan_param (mp,tex_font, fm_cur);
2860         t1_putline (mp);
2861         t1_getline (mp);
2862     }
2863   FOUND:
2864     mp->ps->t1_cs = true;
2865     mp->ps->t1_scan = false;
2866     if (!t1_subrs ())
2867         return;
2868     mp->ps->subr_size_pos = strlen ("/Subrs") + 1;
2869     /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2870     mp->ps->subr_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2871     if (mp->ps->subr_size == 0) {
2872         while (!t1_charstrings ())
2873             t1_getline (mp);
2874         return;
2875     }
2876         /*    |subr_tab = xtalloc (subr_size, cs_entry);| */
2877         mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,mp->ps->subr_size, sizeof (cs_entry));
2878     for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2879         init_cs_entry (ptr);
2880     mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2881     t1_getline (mp);
2882     while (mp->ps->t1_cslen) {
2883         store_subr (mp);
2884         t1_getline (mp);
2885     }
2886     /* mark the first four entries without parsing */
2887     for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2888         mp->ps->subr_tab[i].is_used = true;
2889     /* the end of the Subrs array might have more than one line so we need to
2890        concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2891        the Subrs array followed by the CharStrings dict immediately (synthetic
2892        fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2893        we will treat the font as synthetic and ignore everything until next
2894        Subrs is found
2895      */
2896 #define POST_SUBRS_SCAN     5
2897     s = 0;
2898     *mp->ps->t1_buf_array = 0;
2899     for (i = 0; i < POST_SUBRS_SCAN; i++) {
2900         if (t1_charstrings ())
2901             break;
2902         s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2903         alloc_array (t1_buf, s, T1_BUF_SIZE);
2904         strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2905         t1_getline (mp);
2906     }
2907     mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2908     if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2909                                    suppose synthetic font */
2910         for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2911             if (ptr->valid)
2912                 mp_xfree (ptr->data);
2913         mp_xfree (mp->ps->subr_tab);
2914         mp_xfree (mp->ps->subr_array_start);
2915         mp_xfree (mp->ps->subr_array_end);
2916         cs_init (mp);
2917         mp->ps->t1_cs = false;
2918         mp->ps->t1_synthetic = true;
2919         while (!(t1_charstrings () || t1_subrs ()))
2920             t1_getline (mp);
2921         goto FOUND;
2922     }
2923 }
2924
2925 @ @c
2926 static void t1_flush_cs (MP mp, boolean is_subr)
2927 {
2928     char *p;
2929     byte *r, *return_cs = NULL;
2930     cs_entry *tab, *end_tab, *ptr;
2931     char *start_line, *line_end;
2932     int count, size_pos;
2933     unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2934     if (is_subr) {
2935         start_line = mp->ps->subr_array_start;
2936         line_end =  mp->ps->subr_array_end;
2937         size_pos =  mp->ps->subr_size_pos;
2938         tab =  mp->ps->subr_tab;
2939         count =  mp->ps->subr_max + 1;
2940         end_tab =  mp->ps->subr_tab + count;
2941     } else {
2942         start_line =  mp->ps->cs_dict_start;
2943         line_end =  mp->ps->cs_dict_end;
2944         size_pos =  mp->ps->cs_size_pos;
2945         tab =  mp->ps->cs_tab;
2946         end_tab =  mp->ps->cs_ptr;
2947         count =  mp->ps->cs_count;
2948     }
2949     mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2950     for (p = start_line; p - start_line < size_pos;)
2951         *mp->ps->t1_line_ptr++ = *p++;
2952     while (isdigit (*p))
2953         p++;
2954     sprintf (mp->ps->t1_line_ptr, "%u", count);
2955     strcat (mp->ps->t1_line_ptr, p);
2956     mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2957     t1_putline (mp);
2958
2959     /* create |return_cs| to replace unsused subr's */
2960     if (is_subr) {
2961         cr = 4330;
2962         cs_len = 0;
2963         return_cs = mp_xmalloc (mp, (mp->ps->t1_lenIV + 1) , sizeof(byte));
2964         if ( mp->ps->t1_lenIV > 0) {
2965             for (cs_len = 0, r = return_cs; cs_len <  mp->ps->t1_lenIV; cs_len++, r++)
2966                 *r = cencrypt (0x00, &cr);
2967             *r = cencrypt (CS_RETURN, &cr);
2968         } else {
2969             *return_cs = CS_RETURN;
2970         }
2971         cs_len++;
2972     }
2973
2974     for (ptr = tab; ptr < end_tab; ptr++) {
2975         if (ptr->is_used) {
2976             if (is_subr)
2977                 sprintf (mp->ps->t1_line_array, "dup %i %u", (int) (ptr - tab),
2978                          ptr->cslen);
2979             else
2980                 sprintf (mp->ps->t1_line_array, "/%s %u", ptr->glyph_name, ptr->cslen);
2981             p = strend (mp->ps->t1_line_array);
2982             memcpy (p, ptr->data, ptr->len);
2983             mp->ps->t1_line_ptr = p + ptr->len;
2984             t1_putline (mp);
2985         } else {
2986             /* replace unsused subr's by |return_cs| */
2987             if (is_subr) {
2988                 sprintf (mp->ps->t1_line_array, "dup %i %u%s ", (int) (ptr - tab),
2989                          cs_len,  mp->ps->cs_token_pair[0]);
2990                 p = strend (mp->ps->t1_line_array);
2991                 memcpy (p, return_cs, cs_len);
2992                 mp->ps->t1_line_ptr = p + cs_len;
2993                 t1_putline (mp);
2994                 sprintf (mp->ps->t1_line_array, " %s",  mp->ps->cs_token_pair[1]);
2995                 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2996                 t1_putline (mp);
2997             }
2998         }
2999         mp_xfree (ptr->data);
3000         if (ptr->glyph_name != notdef)
3001             mp_xfree (ptr->glyph_name);
3002     }
3003     sprintf (mp->ps->t1_line_array, "%s", line_end);
3004     mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3005     t1_putline (mp);
3006     if (is_subr)
3007         mp_xfree (return_cs);
3008     mp_xfree (tab);
3009     mp_xfree (start_line);
3010     mp_xfree (line_end);
3011 }
3012
3013 static void t1_mark_glyphs (MP mp, int tex_font)
3014 {
3015     int i;
3016     char *charset = extra_charset ();
3017     char *g, *s, *r;
3018     cs_entry *ptr;
3019     if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) {  /* mark everything */
3020         if (mp->ps->cs_tab != NULL)
3021             for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3022                 if (ptr->valid)
3023                     ptr->is_used = true;
3024         if (mp->ps->subr_tab != NULL) {
3025             for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3026                 if (ptr->valid)
3027                     ptr->is_used = true;
3028             mp->ps->subr_max = mp->ps->subr_size - 1;
3029         }
3030         return;
3031     }
3032     mark_cs (mp,notdef);
3033     for (i = 0; i < 256; i++)
3034         if (is_used_char (i)) {
3035             if (mp->ps->t1_glyph_names[i] == notdef) {
3036                 char S[128];
3037                 mp_snprintf(S,128, "character %i is mapped to %s", i, notdef);
3038                 mp_warn(mp,S);
3039             } else
3040                 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3041         }
3042     if (charset == NULL)
3043         goto SET_SUBR_MAX;
3044     g = s = charset + 1;        /* skip the first '/' */
3045     r = strend (g);
3046     while (g < r) {
3047         while (*s != '/' && s < r)
3048             s++;
3049         *s = 0;                 /* terminate g by rewriting '/' to 0 */
3050         mark_cs (mp,g);
3051         g = s + 1;
3052     }
3053   SET_SUBR_MAX:
3054     if (mp->ps->subr_tab != NULL)
3055         for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab; 
3056                  ptr - mp->ps->subr_tab < mp->ps->subr_size; 
3057              ptr++)
3058             if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3059                 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3060 }
3061
3062 static void t1_subset_charstrings (MP mp, int tex_font) 
3063 {
3064     cs_entry *ptr;
3065     mp->ps->cs_size_pos =
3066         strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3067         - mp->ps->t1_line_array + 1;
3068     /* |cs_size_pos| points to the number indicating
3069        dict size after "/CharStrings" */
3070     mp->ps->cs_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3071     mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,mp->ps->cs_size, sizeof(cs_entry));
3072     for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3073         init_cs_entry (ptr);
3074     mp->ps->cs_notdef = NULL;
3075     mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3076     t1_getline (mp);
3077     while (mp->ps->t1_cslen) {
3078         store_cs (mp);
3079         t1_getline (mp);
3080     }
3081     mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3082     t1_mark_glyphs (mp,tex_font);
3083     if (mp->ps->subr_tab != NULL) {
3084         if (mp->ps->cs_token_pair == NULL) 
3085             mp_fatal_error
3086                 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3087         t1_subr_flush (mp);
3088     }
3089     for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3090         if (ptr->is_used)
3091             mp->ps->cs_count++;
3092     t1_cs_flush (mp);
3093 }
3094
3095 static void t1_subset_end (MP mp)
3096 {
3097     if (mp->ps->t1_synthetic) {         /* copy to "dup /FontName get exch definefont pop" */
3098         while (!strstr (mp->ps->t1_line_array, "definefont")) {
3099             t1_getline (mp);
3100             t1_putline (mp);
3101         }
3102         while (!t1_end_eexec ())
3103             t1_getline (mp);      /* ignore the rest */
3104         t1_putline (mp);          /* write "mark currentfile closefile" */
3105     } else
3106         while (!t1_end_eexec ()) {      /* copy to "mark currentfile closefile" */
3107             t1_getline (mp);
3108             t1_putline (mp);
3109         }
3110     t1_stop_eexec (mp);
3111     if (fixedcontent) {         /* copy 512 zeros (not needed for PDF) */
3112         while (!t1_cleartomark ()) {
3113             t1_getline (mp);
3114             t1_putline (mp);
3115         }
3116         if (!mp->ps->t1_synthetic)      /* don't check "{restore}if" for synthetic fonts */
3117             t1_check_end (mp);    /* write "{restore}if" if found */
3118     }
3119 }
3120
3121 static int t1_updatefm (MP mp, int f, fm_entry *fm)
3122 {
3123   char *s, *p;
3124   mp->ps->read_encoding_only = true;
3125   if (!t1_open_fontfile (mp,fm,NULL)) {
3126         return 0;
3127   }
3128   t1_scan_only (mp,f, fm);
3129   s = mp_xstrdup(mp,mp->ps->fontname_buf);
3130   p = s;
3131   while (*p != ' ' && *p != 0) 
3132      p++;
3133   *p=0;
3134   fm->ps_name = s;
3135   t1_close_font_file (mp,"");
3136   return 1;
3137 }
3138
3139
3140 static void  writet1 (MP mp, int tex_font, fm_entry *fm_cur) {
3141         int save_selector = mp->selector;
3142     mp_normalize_selector(mp);
3143     mp->ps->read_encoding_only = false;
3144     if (!is_included (fm_cur)) {        /* scan parameters from font file */
3145       if (!t1_open_fontfile (mp,fm_cur,"{"))
3146             return;
3147             t1_scan_only (mp,tex_font, fm_cur);
3148         t1_close_font_file (mp,"}");
3149         return;
3150     }
3151     if (!is_subsetted (fm_cur)) {       /* include entire font */
3152       if (!t1_open_fontfile (mp,fm_cur,"<<"))
3153             return;
3154           t1_include (mp,tex_font,fm_cur);
3155         t1_close_font_file (mp,">>");
3156         return;
3157     }
3158     /* partial downloading */
3159     if (!t1_open_fontfile (mp,fm_cur,"<"))
3160         return;
3161     t1_subset_ascii_part (mp,tex_font,fm_cur);
3162     t1_start_eexec (mp,fm_cur);
3163     cc_init ();
3164     cs_init (mp);
3165     t1_read_subrs (mp,tex_font, fm_cur);
3166     t1_subset_charstrings (mp,tex_font);
3167     t1_subset_end (mp);
3168     t1_close_font_file (mp,">");
3169     mp->selector = save_selector; 
3170 }
3171
3172 @ @<Declarations@>=
3173 static void t1_free (MP mp);
3174
3175 @ @c
3176 static void  t1_free (MP mp) {
3177   mp_xfree (mp->ps->t1_line_array);
3178   mp_xfree (mp->ps->t1_buf_array);
3179 }
3180
3181
3182 @* \[44d] Embedding fonts.
3183
3184 @ The |tfm_num| is officially of type |font_number|, but that
3185 type does not exist yet at this point in the output order.
3186
3187 @<Types...@>=
3188 typedef struct {
3189     char *tfm_name;             /* TFM file name */
3190     char *ps_name;              /* PostScript name */
3191     integer flags;              /* font flags */
3192     char *ff_name;              /* font file name */
3193     char *subset_tag;           /* pseudoUniqueTag for subsetted font */
3194     enc_entry *encoding;        /* pointer to corresponding encoding */
3195     unsigned int tfm_num;       /* number of the TFM refering this entry */
3196     unsigned short type;        /* font type (T1/TTF/...) */
3197     short slant;                /* SlantFont */
3198     short extend;               /* ExtendFont */
3199     integer ff_objnum;          /* FontFile object number */
3200     integer fn_objnum;          /* FontName/BaseName object number */
3201     integer fd_objnum;          /* FontDescriptor object number */
3202     char *charset;              /* string containing used glyphs */
3203     boolean all_glyphs;         /* embed all glyphs? */
3204     unsigned short links;       /* link flags from |tfm_tree| and |ps_tree| */
3205     short tfm_avail;            /* flags whether a tfm is available */
3206     short pid;                  /* Pid for truetype fonts */
3207     short eid;                  /* Eid for truetype fonts */
3208 } fm_entry;
3209
3210
3211
3212 @<Glob...@>=
3213 #define FONTNAME_BUF_SIZE 128
3214 boolean fontfile_found;
3215 boolean is_otf_font;
3216 char fontname_buf[FONTNAME_BUF_SIZE];
3217
3218
3219 @d F_INCLUDED          0x01
3220 @d F_SUBSETTED         0x02
3221 @d F_TRUETYPE          0x04
3222 @d F_BASEFONT          0x08
3223
3224 @d set_included(fm)    ((fm)->type |= F_INCLUDED)
3225 @d set_subsetted(fm)   ((fm)->type |= F_SUBSETTED)
3226 @d set_truetype(fm)    ((fm)->type |= F_TRUETYPE)
3227 @d set_basefont(fm)    ((fm)->type |= F_BASEFONT)
3228
3229 @d is_included(fm)     ((fm)->type & F_INCLUDED)
3230 @d is_subsetted(fm)    ((fm)->type & F_SUBSETTED)
3231 @d is_truetype(fm)     ((fm)->type & F_TRUETYPE)
3232 @d is_basefont(fm)     ((fm)->type & F_BASEFONT)
3233 @d is_reencoded(fm)    ((fm)->encoding != NULL)
3234 @d is_fontfile(fm)     (fm_fontfile(fm) != NULL)
3235 @d is_t1fontfile(fm)   (is_fontfile(fm) && !is_truetype(fm))
3236
3237 @d fm_slant(fm)        (fm)->slant
3238 @d fm_extend(fm)       (fm)->extend
3239 @d fm_fontfile(fm)     (fm)->ff_name
3240
3241 @<Declarations@>=
3242 static boolean mp_font_is_reencoded (MP mp, int f);
3243 static boolean mp_font_is_included (MP mp, int f);
3244 static boolean mp_font_is_subsetted (MP mp, int f);
3245
3246 @ @c
3247 static boolean mp_font_is_reencoded (MP mp, int f) {
3248   fm_entry *fm;
3249   if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) { 
3250     if (fm != NULL 
3251         && (fm->ps_name != NULL)
3252         && is_reencoded (fm))
3253       return 1;
3254   }
3255   return 0;
3256 }
3257 static boolean mp_font_is_included (MP mp, int f) {
3258   fm_entry *fm;
3259   if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) { 
3260     if (fm != NULL 
3261         && (fm->ps_name != NULL && fm->ff_name != NULL) 
3262         && is_included (fm))
3263       return 1;
3264   }
3265   return 0;
3266 }
3267 static boolean mp_font_is_subsetted (MP mp, int f) {
3268   fm_entry *fm;
3269   if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) { 
3270     if (fm != NULL 
3271           && (fm->ps_name != NULL && fm->ff_name != NULL) 
3272           && is_included (fm) && is_subsetted (fm))
3273       return 1;
3274   }
3275   return 0;
3276 }
3277
3278 @ @<Exported function headers@>=
3279 char * mp_fm_encoding_name (MP mp, int f);
3280 char * mp_fm_font_name (MP mp, int f);
3281
3282 @ @<Declarations@>=
3283 static char * mp_fm_font_subset_name (MP mp, int f);
3284
3285
3286 @c char * mp_fm_encoding_name (MP mp, int f) {
3287   enc_entry *e;
3288   fm_entry *fm;
3289   if (mp_has_fm_entry (mp, f, &fm)) { 
3290     if (fm != NULL && (fm->ps_name != NULL)) {
3291       if (is_reencoded (fm)) {
3292             e = fm->encoding;
3293         if (e->enc_name!=NULL)
3294           return mp_xstrdup(mp,e->enc_name);
3295       } else {
3296             return NULL;
3297       }
3298     }
3299   }
3300   print_err ("fontmap encoding problems for font ");
3301   mp_print(mp,mp->font_name[f]);
3302   mp_error(mp); 
3303   return NULL;
3304 }
3305 char * mp_fm_font_name (MP mp, int f) {
3306   fm_entry *fm;
3307   if (mp_has_fm_entry (mp, f,&fm)) { 
3308     if (fm != NULL && (fm->ps_name != NULL)) {
3309       if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3310         /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3311         if (t1_updatefm(mp,f,fm)) {
3312           mp->font_ps_name_fixed[f] = true;
3313         } else {
3314           print_err ("font loading problems for font ");
3315           mp_print(mp,mp->font_name[f]);
3316           mp_error(mp);
3317         }
3318       }
3319       return mp_xstrdup(mp,fm->ps_name);
3320     }
3321   }
3322   print_err ("fontmap name problems for font ");
3323   mp_print(mp,mp->font_name[f]);
3324   mp_error(mp); 
3325   return NULL;
3326 }
3327
3328 static char * mp_fm_font_subset_name (MP mp, int f) {
3329   fm_entry *fm;
3330   if (mp_has_fm_entry (mp, f, &fm)) { 
3331     if (fm != NULL && (fm->ps_name != NULL)) {
3332       if (is_subsetted(fm)) {
3333             char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3334         mp_snprintf(s,strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3335             return s;
3336       } else {
3337         return mp_xstrdup(mp,fm->ps_name);
3338       }
3339     }
3340   }
3341   print_err ("fontmap name problems for font ");
3342   mp_print(mp,mp->font_name[f]);
3343   mp_error(mp); 
3344   return NULL;
3345 }
3346
3347 @ @<Declarations@>=
3348 static integer mp_fm_font_slant (MP mp, int f);
3349 static integer mp_fm_font_extend (MP mp, int f);
3350
3351
3352 @c static integer mp_fm_font_slant (MP mp, int f) {
3353   fm_entry *fm;
3354   if (mp_has_fm_entry (mp, f, &fm)) { 
3355     if (fm != NULL && (fm->ps_name != NULL)) {
3356       return fm->slant;
3357     }
3358   }
3359   return 0;
3360 }
3361 static integer mp_fm_font_extend (MP mp, int f) {
3362   fm_entry *fm;
3363   if (mp_has_fm_entry (mp, f, &fm)) { 
3364     if (fm != NULL && (fm->ps_name != NULL)) {
3365       return fm->extend;
3366     }
3367   }
3368   return 0;
3369 }
3370
3371 @ @<Declarations@>=
3372 static boolean mp_do_ps_font (MP mp, font_number f);
3373
3374 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3375   fm_entry *fm_cur;
3376   (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3377   if (fm_cur == NULL)
3378     return 1;
3379   if (is_truetype(fm_cur) ||
3380          (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3381     return 0;
3382   }
3383   if (is_included(fm_cur)) {
3384     mp_ps_print_nl(mp,"%%BeginResource: font ");
3385     if (is_subsetted(fm_cur)) {
3386       mp_ps_print(mp, fm_cur->subset_tag);
3387       mp_ps_print_char(mp,'-');
3388     }
3389     mp_ps_print(mp, fm_cur->ps_name);
3390     mp_ps_print_ln(mp);
3391     writet1 (mp,f,fm_cur);
3392     mp_ps_print_nl(mp,"%%EndResource");
3393     mp_ps_print_ln(mp);
3394   }
3395   return 1;
3396 }
3397
3398 @ Included subset fonts do not need and encoding vector, make
3399 sure we skip that case.
3400
3401 @<Declarations@>=
3402 static void mp_list_used_resources (MP mp, int prologues, int procset);
3403
3404 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
3405   font_number f; /* fonts used in a text node or as loop counters */
3406   int ff;  /* a loop counter */
3407   font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3408   boolean firstitem;
3409   if ( procset>0 )
3410     mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
3411   else
3412     mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3413   ldf=null_font;
3414   firstitem=true;
3415   for (f=null_font+1;f<=mp->last_fnum;f++) {
3416     if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3417           for (ff=ldf;ff>=null_font;ff--) {
3418         if ( mp_has_font_size(mp,ff) )
3419           if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3420             goto FOUND;
3421       }
3422       if ( mp_font_is_subsetted(mp,f) )
3423         goto FOUND;
3424       if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
3425            (unsigned)mp->max_print_line )
3426         mp_ps_print_nl(mp, "%%+ encoding");
3427       if ( firstitem ) {
3428         firstitem=false;
3429         mp_ps_print_nl(mp, "%%+ encoding");
3430       }
3431       mp_ps_print_char(mp, ' ');
3432       mp_ps_print(mp, mp->font_enc_name[f]);
3433       ldf=f;
3434     }
3435   FOUND:
3436     ;
3437   }
3438   ldf=null_font;
3439   firstitem=true;
3440   for (f=null_font+1;f<=mp->last_fnum;f++) {
3441     if ( mp_has_font_size(mp,f) ) {
3442       for (ff=ldf;ff>=null_font;ff--) {
3443         if ( mp_has_font_size(mp,ff) )
3444           if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3445             goto FOUND2;
3446       }
3447       if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
3448                (unsigned)mp->max_print_line )
3449         mp_ps_print_nl(mp, "%%+ font");
3450       if ( firstitem ) {
3451         firstitem=false;
3452         mp_ps_print_nl(mp, "%%+ font");
3453       }
3454       mp_ps_print_char(mp, ' ');
3455           if ( (prologues==3)&&
3456            (mp_font_is_subsetted(mp,f)) )
3457         mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3458       else
3459         mp_ps_print(mp, mp->font_ps_name[f]);
3460       ldf=f;
3461     }
3462   FOUND2:
3463     ;
3464   }
3465   mp_ps_print_ln(mp);
3466
3467
3468 @ @<Declarations@>=
3469 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
3470
3471 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
3472   font_number f; /* fonts used in a text node or as loop counters */
3473   int ff; /* a loop counter */
3474   font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3475   boolean firstitem;
3476   if ( procset>0 )
3477     mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3478   else
3479     mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3480   ldf=null_font;
3481   firstitem=true;
3482   for (f=null_font+1;f<=mp->last_fnum;f++) {
3483     if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) )  {
3484        for (ff=ldf;ff>= null_font;ff++) {
3485          if ( mp_has_font_size(mp,ff) )
3486            if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3487              goto FOUND;
3488         }
3489       if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3490         goto FOUND;
3491       if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(unsigned)mp->max_print_line )
3492         mp_ps_print_nl(mp, "%%+ encoding");
3493       if ( firstitem ) {
3494         firstitem=false;
3495         mp_ps_print_nl(mp, "%%+ encoding");
3496       }
3497       mp_ps_print_char(mp, ' ');
3498       mp_ps_print(mp, mp->font_enc_name[f]);
3499       ldf=f;
3500     }
3501   FOUND:
3502     ;
3503   }
3504   ldf=null_font;
3505   firstitem=true;
3506   if (prologues==3) {
3507     for (f=null_font+1;f<=mp->last_fnum;f++) {
3508       if ( mp_has_font_size(mp,f) ) {
3509         for (ff=ldf;ff>= null_font;ff--) {
3510           if ( mp_has_font_size(mp,ff) )
3511             if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3512                goto FOUND2;
3513         }
3514         if ( ! mp_font_is_included(mp,f) )
3515           goto FOUND2;
3516         if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3517           mp_ps_print_nl(mp, "%%+ font");
3518         if ( firstitem ) {
3519           firstitem=false;
3520           mp_ps_print_nl(mp, "%%+ font");
3521         }
3522         mp_ps_print_char(mp, ' ');
3523             if ( mp_font_is_subsetted(mp,f) ) 
3524           mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3525         else
3526           mp_ps_print(mp, mp->font_ps_name[f]);
3527         ldf=f;
3528       }
3529     FOUND2:
3530       ;
3531     }
3532     mp_ps_print_ln(mp);
3533   }
3534 }
3535
3536 @ @<Declarations...@>=
3537 static void mp_list_needed_resources (MP mp, int prologues);
3538
3539 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
3540   font_number f; /* fonts used in a text node or as loop counters */
3541   int ff; /* a loop counter */
3542   font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3543   boolean firstitem;
3544   ldf=null_font;
3545   firstitem=true;
3546   for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3547     if ( mp_has_font_size(mp,f)) {
3548       for (ff=ldf;ff>=null_font;ff--) {
3549         if ( mp_has_font_size(mp,ff) )
3550           if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3551              goto FOUND;
3552       };
3553       if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3554         goto FOUND;
3555       if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3556         mp_ps_print_nl(mp, "%%+ font");
3557       if ( firstitem ) {
3558         firstitem=false;
3559         mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
3560       }
3561       mp_ps_print_char(mp, ' ');
3562       mp_ps_print(mp, mp->font_ps_name[f]);
3563       ldf=f;
3564     }
3565   FOUND:
3566     ;
3567   }
3568   if ( ! firstitem ) {
3569     mp_ps_print_ln(mp);
3570     ldf=null_font;
3571     firstitem=true;
3572     for (f=null_font+1;f<= mp->last_fnum;f++) {
3573       if ( mp_has_font_size(mp,f) ) {
3574         for (ff=ldf;ff>=null_font;ff-- ) {
3575           if ( mp_has_font_size(mp,ff) )
3576             if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3577               goto FOUND2;
3578         }
3579         if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3580           goto FOUND2;
3581         mp_ps_print(mp, "%%IncludeResource: font ");
3582         mp_ps_print(mp, mp->font_ps_name[f]);
3583         mp_ps_print_ln(mp);
3584         ldf=f;
3585       }
3586     FOUND2:
3587       ;
3588     }
3589   }
3590 }
3591
3592 @ @<Declarations@>=
3593 static void mp_write_font_definition (MP mp, font_number f, int prologues);
3594
3595
3596
3597 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3598     ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3599
3600 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
3601   if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3602        (mp_fm_font_extend(mp,f)!=0)||
3603        (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3604        (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3605     if ( (mp_font_is_subsetted(mp,f))&&
3606          (mp_font_is_included(mp,f))&&(prologues==3))
3607       mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3608     else 
3609       mp_ps_name_out(mp, mp->font_ps_name[f],true);
3610     mp_ps_print(mp, " fcp");
3611     mp_ps_print_ln(mp);
3612     if ( applied_reencoding(f) ) {
3613       mp_ps_print(mp, "/Encoding ");
3614       mp_ps_print(mp, mp->font_enc_name[f]);
3615       mp_ps_print(mp, " def ");
3616     };
3617     if ( mp_fm_font_slant(mp,f)!=0 ) {
3618       mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
3619       mp_ps_print(mp, " SlantFont ");
3620     };
3621     if ( mp_fm_font_extend(mp,f)!=0 ) {
3622       mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3623       mp_ps_print(mp, " ExtendFont ");
3624     };
3625     if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3626       mp_ps_print(mp, " 890 ScaleFont ");
3627       mp_ps_print(mp, " 277 SlantFont ");
3628     };
3629     if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3630       mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3631       mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3632       mp_ps_print(mp, "/space[0 -278]def ");
3633       mp_ps_print(mp, "/a12[-904 -939]def ");
3634       mp_ps_print(mp, "end def ");
3635     };  
3636     mp_ps_print(mp, "currentdict end");
3637     mp_ps_print_ln(mp);
3638     mp_ps_print_defined_name(mp,f,prologues);
3639     mp_ps_print(mp, " exch definefont pop");
3640     mp_ps_print_ln(mp);
3641   }
3642 }
3643
3644 @ @<Declarations@>=
3645 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
3646
3647
3648 @c static void mp_ps_print_defined_name(MP mp, font_number A, int prologues) {
3649   mp_ps_print(mp, " /");
3650   if ((mp_font_is_subsetted(mp,(A)))&&
3651       (mp_font_is_included(mp,(A)))&&(prologues==3))
3652     mp_ps_print(mp, mp_fm_font_subset_name(mp,(A)));
3653   else 
3654     mp_ps_print(mp, mp->font_ps_name[(A)]);
3655   if ( mp_xstrcmp(mp->font_name[(A)],"psyrgo")==0 )
3656     mp_ps_print(mp, "-Slanted");
3657   if ( mp_xstrcmp(mp->font_name[(A)],"zpzdr-reversed")==0 ) 
3658     mp_ps_print(mp, "-Reverse");
3659   if ( applied_reencoding((A)) ) { 
3660     mp_ps_print(mp, "-");
3661     mp_ps_print(mp, mp->font_enc_name[(A)]); 
3662   }
3663   if ( mp_fm_font_slant(mp,(A))!=0 ) {
3664     mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,(A))) ;
3665   }
3666   if ( mp_fm_font_extend(mp,(A))!=0 ) {
3667     mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,(A))); 
3668   }
3669 }
3670
3671 @ @<Include encodings and fonts for edge structure~|h|@>=
3672 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
3673 @<Embed fonts that are available@>
3674
3675 @ @<Embed fonts that are available@>=
3676
3677 next_size=0;
3678 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3679 do {  
3680   done_fonts=true;
3681   for (f=null_font+1;f<=mp->last_fnum;f++) {
3682     if ( cur_fsize[f]!=null ) {
3683       if (prologues==3 ) {
3684         if ( ! mp_do_ps_font(mp,f) ) {
3685               if ( mp_has_fm_entry(mp,f, NULL) ) {
3686             print_err("Font embedding failed");
3687             mp_error(mp);
3688           }
3689         }
3690       }
3691       cur_fsize[f]=link(cur_fsize[f]);
3692       if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3693     }
3694   }
3695   if ( ! done_fonts )
3696     @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3697       that size index@>;
3698 } while (! done_fonts);
3699 }
3700
3701 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3702
3703   next_size++;
3704   mp_apply_mark_string_chars(mp, h, next_size);
3705 }
3706
3707 @ We also need to keep track of which characters are used in text nodes
3708 in the edge structure that is being shipped out.  This is done by procedures
3709 that use the left-over |b3| field in the |char_info| words; i.e.,
3710 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3711
3712 @<Types...@>=
3713 enum mp_char_mark_state {mp_unused=0, mp_used};
3714
3715 @ @<Exported...@>=
3716 void mp_mark_string_chars (MP mp,font_number f, char *s) ;
3717
3718 @ @c
3719 void mp_mark_string_chars (MP mp,font_number f, char *s) {
3720   integer b; /* |char_base[f]| */
3721   ASCII_code bc,ec; /* only characters between these bounds are marked */
3722   char *k; /* an index into string |s| */
3723   b=mp->char_base[f];
3724   bc=mp->font_bc[f];
3725   ec=mp->font_ec[f];
3726   k=s;
3727   while (*k){ 
3728     if ( (*k>=bc)&&(*k<=ec) )
3729       mp->font_info[b+*k].qqqq.b3=mp_used;
3730     k++;
3731   }
3732 }
3733
3734
3735 @ @<Exported ...@>=
3736 void mp_unmark_font (MP mp,font_number f) ;
3737
3738 @ @c
3739 void mp_unmark_font (MP mp,font_number f) {
3740   int k; /* an index into |font_info| */
3741   for (k= mp->char_base[f]+mp->font_bc[f];
3742        k<=mp->char_base[f]+mp->font_ec[f];
3743        k++)
3744     mp->font_info[k].qqqq.b3=mp_unused;
3745 }
3746
3747
3748 @ @<Exported...@>=
3749 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) ;
3750
3751 @ @c
3752 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
3753   quarterword next_size; /* the size index for fonts being listed */
3754   pointer *cur_fsize; /* current positions in |font_sizes| */
3755   boolean done_fonts; /* have we finished listing the fonts in the header? */
3756   font_number f; /* a font number for loops */
3757   cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3758   mp_list_used_resources(mp, prologues, procset);
3759   mp_list_supplied_resources(mp, prologues, procset);
3760   mp_list_needed_resources(mp, prologues);
3761   mp_ps_print_nl(mp, "%%EndComments");
3762   mp_ps_print_nl(mp, "%%BeginProlog");
3763   if ( procset>0 )
3764     mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
3765   else
3766     mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3767   mp_ps_print_nl(mp, "/bd{bind def}bind def"
3768                   "/fshow {exch findfont exch scalefont setfont show}bd");
3769   if ( procset>0 ) @<Print the procset@>;
3770   mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
3771                   "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3772   mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3773                    "/fmd{/FontMatrix exch def}bd");
3774   mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3775                   "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3776   mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3777                   " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3778   mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3779                       " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3780   mp_ps_print_nl(mp, "%%EndResource");
3781   @<Include encodings and fonts  for edge structure~|h|@>;
3782   mp_ps_print_nl(mp, "%%EndProlog");
3783   mp_ps_print_nl(mp, "%%BeginSetup");
3784   mp_ps_print_ln(mp);
3785   for (f=null_font+1;f<=mp->last_fnum;f++) {
3786     if ( mp_has_font_size(mp,f) ) {
3787       if ( mp_has_fm_entry(mp,f,NULL) ) {
3788         mp_write_font_definition(mp,f, prologues);
3789         mp_ps_name_out(mp, mp->font_name[f],true);
3790         mp_ps_print_defined_name(mp,f, prologues);
3791         mp_ps_print(mp, " def");
3792       } else {
3793             char s[256];
3794         mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3795         mp_warn(mp,s);
3796         mp_ps_name_out(mp, mp->font_name[f],true);
3797         mp_ps_name_out(mp, mp->font_name[f],true);
3798         mp_ps_print(mp, " def");
3799       }
3800       mp_ps_print_ln(mp);
3801     }
3802   }
3803   mp_ps_print_nl(mp, "%%EndSetup");
3804   mp_ps_print_nl(mp, "%%Page: 1 1");
3805   mp_ps_print_ln(mp);
3806   mp_xfree(cur_fsize);
3807 }
3808
3809 @ @<Declarations@>=
3810 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues);
3811
3812
3813
3814 @c 
3815 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) {
3816   quarterword next_size; /* the size index for fonts being listed */
3817   pointer *cur_fsize; /* current positions in |font_sizes| */
3818   int ff; /* a loop counter */
3819   boolean done_fonts; /* have we finished listing the fonts in the header? */
3820   font_number f; /* a font number for loops */
3821   scaled ds; /* design size and scale factor for a text node */
3822   font_number ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3823   cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3824   if ( prologues>0 ) {
3825     @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3826       |font_sizes| and eliminate duplicates@>;
3827   } else { 
3828     next_size=0;
3829     @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3830     do {  done_fonts=true;
3831       for (f=null_font+1;f<=mp->last_fnum;f++) {
3832         if ( cur_fsize[f]!=null ) {
3833           @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3834         }
3835         if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false;  };
3836       }
3837       if ( ! done_fonts ) {
3838         @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3839           that size index@>;
3840       }
3841     } while (! done_fonts);
3842   }
3843   mp_xfree(cur_fsize);
3844   return ldf;
3845 }
3846
3847 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3848 for (f=null_font+1;f<= mp->last_fnum;f++)
3849   cur_fsize[f]=mp->font_sizes[f]
3850
3851 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3852 so we carefully remove duplicates.  There is no harm in using a slow, brute-force
3853 search.
3854
3855 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3856
3857   ldf=null_font;
3858   for (f=null_font+1;f<= mp->last_fnum;f++) {
3859     if ( mp->font_sizes[f]!=null ) {
3860       if ( ldf==null_font ) 
3861         mp_ps_print_nl(mp, "%%DocumentFonts:");
3862       for (ff=ldf;ff>=null_font;ff--) {
3863         if ( mp->font_sizes[ff]!=null )
3864           if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3865             goto FOUND;
3866       }
3867       if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3868         mp_ps_print_nl(mp, "%%+");
3869       mp_ps_print_char(mp, ' ');
3870       mp_ps_print(mp, mp->font_ps_name[f]);
3871       ldf=f;
3872     FOUND:
3873       ;
3874     }
3875   }
3876 }
3877
3878 @ @c
3879 static void mp_hex_digit_out (MP mp,small_number d) { 
3880   if ( d<10 ) mp_ps_print_char(mp, d+'0');
3881   else mp_ps_print_char(mp, d+'a'-10);
3882 }
3883
3884 @ We output the marks as a hexadecimal bit string starting at |c| or
3885 |font_bc[f]|, whichever is greater.  If the output has to be truncated
3886 to avoid exceeding |emergency_line_length| the return value says where to
3887 start scanning next time.
3888
3889 @<Declarations@>=
3890 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3891
3892
3893 @d emergency_line_length 255
3894   /* \ps\ output lines can be this long in unusual circumstances */
3895
3896 @c
3897 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3898   eight_bits bc,ec; /* only encode characters between these bounds */
3899   integer lim; /* the maximum number of marks to encode before truncating */
3900   int p; /* |font_info| index for the current character */
3901   int d,b; /* used to construct a hexadecimal digit */
3902   lim=4*(emergency_line_length-mp->ps->ps_offset-4);
3903   bc=mp->font_bc[f];
3904   ec=mp->font_ec[f];
3905   if ( c>bc ) bc=c;
3906   @<Restrict the range |bc..ec| so that it contains no unused characters
3907     at either end and has length at most |lim|@>;
3908   @<Print the initial label indicating that the bitmap starts at |bc|@>;
3909   @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3910   while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3911     p++; ec++;
3912   }
3913   return (ec+1);
3914 }
3915
3916 @ We could save time by setting the return value before the loop that
3917 decrements |ec|, but there is no point in being so tricky.
3918
3919 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3920 p=mp->char_base[f]+bc;
3921 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3922   p++; bc++;
3923 }
3924 if ( ec>=bc+lim ) ec=bc+lim-1;
3925 p=mp->char_base[f]+ec;
3926 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) { 
3927   p--; ec--;
3928 }
3929
3930 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3931 mp_ps_print_char(mp, ' ');
3932 mp_hex_digit_out(mp, bc / 16);
3933 mp_hex_digit_out(mp, bc % 16);
3934 mp_ps_print_char(mp, ':')
3935
3936
3937
3938 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3939 b=8; d=0;
3940 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3941   if ( b==0 ) {
3942     mp_hex_digit_out(mp, d);
3943     d=0; b=8;
3944   }
3945   if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
3946   b=b>>1;
3947 }
3948 mp_hex_digit_out(mp, d)
3949
3950
3951 @ Here is a simple function that determines whether there are any marked
3952 characters in font~|f| with character code at least~|c|.
3953
3954 @<Declarations@>=
3955 static boolean mp_check_ps_marks (MP mp,font_number f, integer  c) ;
3956
3957 @ @c
3958 static boolean mp_check_ps_marks (MP mp,font_number f, integer  c) {
3959   int p; /* |font_info| index for the current character */
3960   for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
3961     if ( mp->font_info[p].qqqq.b3==mp_used ) 
3962        return true;
3963   }
3964   return false;
3965 }
3966
3967
3968 @ If the file name is so long that it can't be printed without exceeding
3969 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
3970 line.  We might have to repeat line in order to get the character usage
3971 information to fit within |emergency_line_length|.
3972
3973 TODO: these two defines are also defined in mp.w!
3974
3975 @d link(A)   mp->mem[(A)].hh.rh /* the |link| field of a memory word */
3976 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
3977
3978 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
3979 { integer t=0;
3980   while ( mp_check_ps_marks(mp, f,t) ) {
3981     mp_ps_print_nl(mp, "%*Font: ");
3982     if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
3983       break;
3984     mp_ps_print(mp, mp->font_name[f]);
3985     mp_ps_print_char(mp, ' ');
3986     ds=(mp->font_dsize[f] + 8) / 16;
3987     mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
3988     if ( mp->ps->ps_offset+12>emergency_line_length ) break;
3989     mp_ps_print_char(mp, ' ');
3990     mp_ps_print_scaled(mp, ds);
3991     if ( mp->ps->ps_offset+5>emergency_line_length ) break;
3992     t=mp_ps_marks_out(mp, f,t);
3993   }
3994   cur_fsize[f]=link(cur_fsize[f]);
3995 }
3996
3997 @ @<Print the procset@>=
3998 {
3999   mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
4000   mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
4001   mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
4002                   "/p{closepath}bd/n{newpath}bd");
4003   mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
4004                   "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
4005   mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
4006                   "/Q{grestore}bd/s{scale}bd/t{concat}bd");
4007   mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4008 }
4009
4010
4011 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4012 arguments use |font_name| instead of |font_ps_name|.  Downloaded bitmap fonts
4013 might not have reasonable |font_ps_name| entries, but we just charge ahead
4014 anyway.  The user should not make \&{prologues} positive if this will cause
4015 trouble.
4016 @:prologues_}{\&{prologues} primitive@>
4017
4018 @<Exported...@>=
4019 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset);
4020
4021 @ @c 
4022 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
4023   font_number f;
4024   font_number ldf ;
4025   ldf = mp_print_font_comments (mp, h, prologues);
4026   mp_ps_print_ln(mp);
4027   if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
4028     mp_read_psname_table(mp);
4029   mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4030   if ( (prologues>0)||(procset>0) ) {
4031     if ( ldf!=null_font ) {
4032       if ( prologues>0 ) {
4033         for (f=null_font+1;f<=mp->last_fnum;f++) {
4034           if ( mp_has_font_size(mp,f) ) {
4035             mp_ps_name_out(mp, mp->font_name[f],true);
4036             mp_ps_name_out(mp, mp->font_ps_name[f],true);
4037             mp_ps_print(mp, " def");
4038             mp_ps_print_ln(mp);
4039           }
4040         }
4041         if ( procset==0 ) {
4042           mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4043           mp_ps_print_ln(mp);
4044         }
4045       }
4046     }
4047     if (procset>0 ) {
4048       mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4049       if ( (prologues>0)&&(ldf!=null_font) )
4050         mp_ps_print_nl(mp, 
4051         "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4052       else
4053         mp_ps_print_nl(mp, "/bd{bind def}bind def");
4054       @<Print the procset@>;
4055       mp_ps_print_nl(mp, "%%EndResource");
4056       mp_ps_print_ln(mp);
4057     }
4058   }
4059   mp_ps_print(mp, "%%EndProlog");
4060   mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4061 }
4062
4063 @ \MP\ used to have one single routine to print to both `write' files
4064 and the PostScript output. Web2c redefines ``Character |k| cannot be
4065 printed'', and that resulted in some bugs where 8-bit characters were
4066 written to the PostScript file (reported by Wlodek Bzyl).
4067
4068 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4069 a plain space, since that makes it easier to parse the result file
4070 for postprocessing.
4071
4072 @<Character |k| is not allowed in PostScript output@>=
4073   (k<=' ')||(k>'~')
4074
4075 @ We often need to print a pair of coordinates.
4076
4077 @c
4078 void mp_ps_pair_out (MP mp,scaled x, scaled y) { 
4079   ps_room(26);
4080   mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
4081   mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
4082 }
4083
4084 @ @<Exported...@>=
4085 void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4086
4087 @ @c
4088 void mp_ps_print_cmd (MP mp, const char *l, const char *s) {
4089   if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4090   else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4091 }
4092
4093 @ @<Exported...@>=
4094 void mp_ps_print_cmd (MP mp, const char *l, const char *s) ;
4095
4096 @ @c
4097 void mp_ps_string_out (MP mp, const char *s) {
4098   ASCII_code k; /* bits to be converted to octal */
4099   mp_ps_print(mp, "(");
4100   while ((k=*s++)) {
4101     if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4102       mp_ps_print_char(mp, '\\');
4103       mp_ps_print_ln(mp);
4104     }
4105     if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4106       mp_ps_print_char(mp, '\\');
4107       mp_ps_print_char(mp, '0'+(k / 64));
4108       mp_ps_print_char(mp, '0'+((k / 8) % 8));
4109       mp_ps_print_char(mp, '0'+(k % 8));
4110     } else { 
4111       if ( (k=='(')||(k==')')||(k=='\\') ) 
4112         mp_ps_print_char(mp, '\\');
4113       mp_ps_print_char(mp, k);
4114     }
4115   }
4116   mp_ps_print_char(mp, ')');
4117 }
4118
4119 @ @<Exported...@>=
4120 void mp_ps_string_out (MP mp, const char *s) ;
4121
4122 @ This is a define because the function does not use its |mp| argument.
4123
4124 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4125
4126 @c
4127 static boolean mp_do_is_ps_name (char *s) {
4128   ASCII_code k; /* the character being checked */
4129   while ((k=*s++)) {
4130     if ( (k<=' ')||(k>'~') ) return false;
4131     if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4132        (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4133   }
4134   return true;
4135 }
4136
4137 @ @<Exported...@>=
4138 void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4139
4140 @ @c
4141 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4142   ps_room(strlen(s)+2);
4143   mp_ps_print_char(mp, ' ');
4144   if ( mp_is_ps_name(mp, s) ) {
4145     if ( lit ) mp_ps_print_char(mp, '/');
4146       mp_ps_print(mp, s);
4147   } else { 
4148     mp_ps_string_out(mp, s);
4149     if ( ! lit ) mp_ps_print(mp, "cvx ");
4150       mp_ps_print(mp, "cvn");
4151   }
4152 }
4153
4154
4155 @ These special comments described in the {\sl PostScript Language Reference
4156 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4157 We can't normally output ``conforming'' \ps\ because
4158 the structuring conventions don't allow us to say ``Please make sure the
4159 following characters are downloaded and define the \.{fshow} macro to access
4160 them.''
4161
4162 The exact bounding box is written out if |mp_prologues<0|, although this
4163 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4164 accurately. (Overfull boxes are avoided if an illustration is made to
4165 match a given \.{\char`\\hsize}.)
4166
4167 @<Exported...@>=
4168 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues);
4169
4170 @ @c
4171 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) {
4172   scaled t;
4173   mp_ps_print(mp, "%!PS");
4174   if ( prologues>0 ) 
4175     mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
4176   mp_ps_print_nl(mp, "%%BoundingBox: ");
4177   if ( hh->_minx>hh->_maxx) {
4178      mp_ps_print(mp, "0 0 0 0");
4179   } else if ( prologues<0 ) {
4180     mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4181     mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4182   } else { 
4183     mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->_minx),mp_floor_scaled(mp, hh->_miny));
4184     mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->_maxx),-mp_floor_scaled(mp, -hh->_maxy));
4185   }
4186   mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
4187   if ( hh->_minx>hh->_maxx ) {
4188     mp_ps_print(mp, "0 0 0 0");
4189   } else {
4190     mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4191     mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4192   }
4193   mp_ps_print_nl(mp, "%%Creator: MetaPost ");
4194   mp_ps_print(mp, mp_metapost_version());
4195   mp_ps_print_nl(mp, "%%CreationDate: ");
4196   mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year])); 
4197   mp_ps_print_char(mp, '.');
4198   mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month])); 
4199   mp_ps_print_char(mp, '.');
4200   mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day])); 
4201   mp_ps_print_char(mp, ':');
4202   t=mp_round_unscaled(mp, mp->internal[mp_time]);
4203   mp_ps_print_dd(mp, t / 60); 
4204   mp_ps_print_dd(mp, t % 60);
4205   mp_ps_print_nl(mp, "%%Pages: 1");
4206 }
4207
4208 @ The most important output procedure is the one that gives the \ps\ version of
4209 a \MP\ path.
4210
4211 @(psout.h@>=
4212 typedef struct mp_knot {
4213   unsigned short left_type_field;
4214   unsigned short right_type_field;
4215   signed int x_coord_field;
4216   signed int y_coord_field;
4217   signed int left_x_field;
4218   signed int left_y_field;
4219   signed int right_x_field;
4220   signed int right_y_field;
4221   struct mp_knot * next_field;
4222   unsigned char originator_field;
4223 } mp_knot;
4224
4225 @ @<Types...@>=
4226 #define gr_left_type(A)  (A)->left_type_field 
4227 #define gr_right_type(A) (A)->right_type_field
4228 #define gr_x_coord(A)    (A)->x_coord_field   
4229 #define gr_y_coord(A)    (A)->y_coord_field   
4230 #define gr_left_x(A)     (A)->left_x_field    
4231 #define gr_left_y(A)     (A)->left_y_field    
4232 #define gr_right_x(A)    (A)->right_x_field   
4233 #define gr_right_y(A)    (A)->right_y_field   
4234 #define gr_next_knot(A)  (A)->next_field
4235 #define gr_originator(A) (A)->originator_field
4236
4237 @ @c
4238 mp_knot * mp_gr_insert_knot (MP mp, mp_knot *q, scaled x, scaled y) {
4239   /* returns the inserted knot */
4240   mp_knot *r; /* the new knot */
4241   r= mp_xmalloc(mp, 1, sizeof (mp_knot));
4242   gr_next_knot(r)=gr_next_knot(q); gr_next_knot(q)=r;
4243   gr_right_x(r)=gr_right_x(q);
4244   gr_right_y(r)=gr_right_y(q);
4245   gr_x_coord(r)=x;
4246   gr_y_coord(r)=y;
4247   gr_right_x(q)=gr_x_coord(q);
4248   gr_right_y(q)=gr_y_coord(q);
4249   gr_left_x(r)=gr_x_coord(r);
4250   gr_left_y(r)=gr_y_coord(r);
4251   gr_left_type(r)=mp_explicit;
4252   gr_right_type(r)=mp_explicit;
4253   gr_originator(r)=mp_program_code;
4254   return r;
4255 }
4256
4257
4258 @ If we want to duplicate a knot node, we can say |copy_knot|:
4259
4260 @c 
4261 mp_knot *mp_gr_copy_knot (MP mp,  mp_knot *p) {
4262   mp_knot *q; /* the copy */
4263   q = mp_xmalloc(mp, 1, sizeof (mp_knot));
4264   memcpy(q,p,sizeof (mp_knot));
4265   gr_next_knot(q)=NULL;
4266   return q;
4267 }
4268
4269 @ The |copy_path| routine makes a clone of a given path.
4270
4271 @c 
4272 mp_knot *mp_gr_copy_path (MP mp,  mp_knot *p) {
4273   mp_knot *q, *pp, *qq; /* for list manipulation */
4274   if (p==NULL) 
4275     return NULL;
4276   q=mp_gr_copy_knot(mp, p);
4277   qq=q; 
4278   pp=gr_next_knot(p);
4279   while ( pp!=p ) { 
4280     gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4281     qq=gr_next_knot(qq);
4282     pp=gr_next_knot(pp);
4283   }
4284   gr_next_knot(qq)=q;
4285   return q;
4286 }
4287
4288 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4289 returns a pointer to the first node of the copy, if the path is a cycle,
4290 but to the final node of a non-cyclic copy. The global
4291 variable |path_tail| will point to the final node of the original path;
4292 this trick makes it easier to implement `\&{doublepath}'.
4293
4294 All node types are assumed to be |endpoint| or |explicit| only.
4295
4296 This function is currenly unused.
4297
4298 @c 
4299 mp_knot * mp_gr_htap_ypoc (MP mp,  mp_knot *p) {
4300   mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4301   q=mp_xmalloc(mp, 1, sizeof (mp_knot)); /* this will correspond to |p| */
4302   qq=q; pp=p;
4303   while (1) { 
4304     gr_right_type(qq)=gr_left_type(pp); 
4305     gr_left_type(qq)=gr_right_type(pp);
4306     gr_x_coord(qq)=gr_x_coord(pp); 
4307     gr_y_coord(qq)=gr_y_coord(pp);
4308     gr_right_x(qq)=gr_left_x(pp); 
4309     gr_right_y(qq)=gr_left_y(pp);
4310     gr_left_x(qq)=gr_right_x(pp); 
4311     gr_left_y(qq)=gr_right_y(pp);
4312     gr_originator(qq)=gr_originator(pp);
4313     if ( gr_next_knot(pp)==p ) { 
4314       gr_next_knot(q)=qq; 
4315       /* |mp->path_tail=pp;| */ /* ? */
4316       return q;
4317     }
4318     rr=mp_xmalloc(mp, 1, sizeof (mp_knot));
4319     gr_next_knot(rr)=qq; 
4320     qq=rr; 
4321     pp=gr_next_knot(pp);
4322   }
4323 }
4324
4325 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4326 calling the following subroutine.
4327
4328 @<Declarations@>=
4329 void mp_do_gr_toss_knot_list (mp_knot *p) ;
4330
4331
4332 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4333
4334 @c
4335 void mp_do_gr_toss_knot_list (mp_knot * p) {
4336   mp_knot *q; /* the node being freed */
4337   mp_knot *r; /* the next node */
4338   if (p==NULL)
4339     return;
4340   q=p;
4341   do {  
4342     r=gr_next_knot(q); 
4343     mp_xfree(q); q=r;
4344   } while (q!=p);
4345 }
4346
4347
4348
4349 @ @c
4350 void mp_gr_ps_path_out (MP mp, mp_knot *h) {
4351   mp_knot *p, *q; /* for scanning the path */
4352   scaled d; /* a temporary value */
4353   boolean curved; /* |true| unless the cubic is almost straight */
4354   ps_room(40);
4355   mp_ps_print_cmd(mp, "newpath ","n ");
4356   mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4357   mp_ps_print_cmd(mp, "moveto","m");
4358   p=h;
4359   do {  
4360     if ( gr_right_type(p)==mp_endpoint ) { 
4361       if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4362       return;
4363     }
4364     q=gr_next_knot(p);
4365     @<Start a new line and print the \ps\ commands for the curve from
4366       |p| to~|q|@>;
4367     p=q;
4368   } while (p!=h);
4369   mp_ps_print_cmd(mp, " closepath"," p");
4370 }
4371
4372 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4373 curved=true;
4374 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4375 mp_ps_print_ln(mp);
4376 if ( curved ){ 
4377   mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4378   mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4379   mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4380   mp_ps_print_cmd(mp, "curveto","c");
4381 } else if ( q!=h ){ 
4382   mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4383   mp_ps_print_cmd(mp, "lineto","l");
4384 }
4385
4386 @ Two types of straight lines come up often in \MP\ paths:
4387 cubics with zero initial and final velocity as created by |make_path| or
4388 |make_envelope|, and cubics with control points uniformly spaced on a line
4389 as created by |make_choices|.
4390
4391 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4392
4393 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4394 if ( gr_right_x(p)==gr_x_coord(p) )
4395   if ( gr_right_y(p)==gr_y_coord(p) )
4396     if ( gr_left_x(q)==gr_x_coord(q) )
4397       if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4398 d=gr_left_x(q)-gr_right_x(p);
4399 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4400   if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4401     { d=gr_left_y(q)-gr_right_y(p);
4402     if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4403       if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4404     }
4405
4406 @ The colored objects use a struct with anonymous fields to express the color parts:
4407
4408 @(psout.h@>=
4409 typedef struct {
4410    int _a_val, _b_val, _c_val, _d_val;
4411 } mp_color;
4412
4413 @ The exported form of a dash pattern is simpler than the internal
4414 format, it is closely modelled to the PostScript model. The array of
4415 dashes is ended by a single negative value, because this is not
4416 allowed in PostScript.
4417
4418 @(psout.h@>=
4419 typedef struct {
4420   int offset_field;
4421   int *array_field;
4422 } mp_dash_object ;
4423
4424
4425
4426 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B) 
4427
4428 @<Declarations@>=
4429 void mp_do_gr_toss_dashes(mp_dash_object *dl);
4430
4431 @ @c
4432 void mp_do_gr_toss_dashes(mp_dash_object *dl) {
4433   if (dl==NULL)   
4434     return;
4435   mp_xfree(dl->array_field);  
4436   mp_xfree(dl);
4437 }
4438
4439
4440 @ @c
4441 mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) {
4442         mp_dash_object *q = NULL;
4443     (void)mp;
4444         if (dl==NULL)
4445       return NULL;
4446         q = mp_xmalloc(mp, 1, sizeof (mp_dash_object));
4447         memcpy (q,dl,sizeof(mp_dash_object));
4448         if (dl->array_field != NULL) {
4449           int i = 0;
4450       while (*(dl->array_field+i) != -1) i++;
4451           q->array_field = mp_xmalloc(mp, i, sizeof (scaled));
4452           memcpy(q->array_field,dl->array_field, (i*sizeof(scaled)));
4453     }
4454         return q;
4455 }
4456
4457
4458 @ Now for outputting the actual graphic objects. First, set up some 
4459 structures and access macros.
4460
4461 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4462
4463 @<Types...@>=
4464 #define gr_type(A)         (A)->_type_field
4465 #define gr_link(A)         (A)->_link_field
4466 #define gr_color_model(A)  (A)->color_model_field
4467 #define gr_red_val(A)      (A)->color_field._a_val
4468 #define gr_green_val(A)    (A)->color_field._b_val
4469 #define gr_blue_val(A)     (A)->color_field._c_val
4470 #define gr_cyan_val(A)     (A)->color_field._a_val
4471 #define gr_magenta_val(A)  (A)->color_field._b_val
4472 #define gr_yellow_val(A)   (A)->color_field._c_val
4473 #define gr_black_val(A)    (A)->color_field._d_val
4474 #define gr_grey_val(A)     (A)->color_field._a_val
4475 #define gr_path_p(A)       (A)->path_p_field 
4476 #define gr_htap_p(A)       ((mp_fill_object *)A)->htap_p_field 
4477 #define gr_pen_p(A)        (A)->pen_p_field 
4478 #define gr_ljoin_val(A)    (A)->ljoin_field
4479 #define gr_lcap_val(A)     ((mp_stroked_object *)A)->lcap_field
4480 #define gr_miterlim_val(A) (A)->miterlim_field
4481 #define gr_pre_script(A)   (A)->pre_script_field
4482 #define gr_post_script(A)  (A)->post_script_field
4483 #define gr_dash_p(A)       ((mp_stroked_object *)A)->dash_p_field
4484 #define gr_name_type(A)    ((mp_text_object *)A)->name_type_field
4485 #define gr_text_p(A)       ((mp_text_object *)A)->text_p_field 
4486 #define gr_font_n(A)       ((mp_text_object *)A)->font_n_field 
4487 #define gr_font_name(A)    ((mp_text_object *)A)->font_name_field 
4488 #define gr_font_dsize(A)   ((mp_text_object *)A)->font_dsize_field 
4489 #define gr_width_val(A)    ((mp_text_object *)A)->width_field
4490 #define gr_height_val(A)   ((mp_text_object *)A)->height_field
4491 #define gr_depth_val(A)    ((mp_text_object *)A)->depth_field
4492 #define gr_tx_val(A)       ((mp_text_object *)A)->tx_field
4493 #define gr_ty_val(A)       ((mp_text_object *)A)->ty_field
4494 #define gr_txx_val(A)      ((mp_text_object *)A)->txx_field
4495 #define gr_txy_val(A)      ((mp_text_object *)A)->txy_field
4496 #define gr_tyx_val(A)      ((mp_text_object *)A)->tyx_field
4497 #define gr_tyy_val(A)      ((mp_text_object *)A)->tyy_field
4498
4499 @ @(psout.h@>=
4500 #define GRAPHIC_BODY                      \
4501   int _type_field;                   \
4502   struct mp_graphic_object * _link_field
4503
4504 typedef struct mp_graphic_object {
4505   GRAPHIC_BODY;
4506 } mp_graphic_object;
4507
4508 typedef struct mp_text_object {
4509   GRAPHIC_BODY;
4510   char *pre_script_field;
4511   char *post_script_field;
4512   mp_color color_field;
4513   unsigned char color_model_field;
4514   unsigned char name_type_field;
4515   char *text_p_field;
4516   char *font_name_field ;   
4517   unsigned int font_dsize_field ;
4518   unsigned int font_n_field ;   
4519   int width_field ;
4520   int height_field ;
4521   int depth_field ;
4522   int tx_field ;
4523   int ty_field ;
4524   int txx_field ;
4525   int txy_field ;
4526   int tyx_field ;
4527   int tyy_field ;
4528 } mp_text_object;
4529
4530 typedef struct mp_fill_object {
4531   GRAPHIC_BODY;
4532   char *pre_script_field;
4533   char *post_script_field;
4534   mp_color color_field;
4535   unsigned char color_model_field;
4536   unsigned char ljoin_field ;   
4537   mp_knot * path_p_field;
4538   mp_knot * htap_p_field;
4539   mp_knot * pen_p_field;
4540   int miterlim_field ;
4541 } mp_fill_object;
4542
4543 typedef struct mp_stroked_object {
4544   GRAPHIC_BODY;
4545   char *pre_script_field;
4546   char *post_script_field;
4547   mp_color color_field;
4548   unsigned char color_model_field;
4549   unsigned char ljoin_field ;   
4550   unsigned char lcap_field ;   
4551   mp_knot * path_p_field;
4552   mp_knot * pen_p_field;
4553   int miterlim_field ;
4554   mp_dash_object *dash_p_field;
4555 } mp_stroked_object;
4556
4557 typedef struct mp_clip_object {
4558   GRAPHIC_BODY;
4559   mp_knot * path_p_field;
4560 } mp_clip_object;
4561
4562 typedef struct mp_bounds_object {
4563   GRAPHIC_BODY;
4564   mp_knot * path_p_field;
4565 } mp_bounds_object;
4566
4567 typedef struct mp_special_object {
4568   GRAPHIC_BODY;
4569   char *pre_script_field;
4570 } mp_special_object ;
4571
4572 typedef struct mp_edge_object {
4573   struct mp_graphic_object * body;
4574   struct mp_edge_object * _next;
4575   char * _filename;
4576   MP _parent;
4577   int _minx, _miny, _maxx, _maxy;
4578   int _width, _height, _depth, _ital_corr;
4579   int _charcode;
4580 } mp_edge_object;
4581
4582 @ @<Exported function headers@>=
4583 mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4584
4585 @ @c
4586 mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4587   mp_graphic_object *p;
4588   int size ;
4589   switch (type) {
4590   case mp_fill_code:         size = sizeof(mp_fill_object);    break;
4591   case mp_stroked_code:      size = sizeof(mp_stroked_object); break;
4592   case mp_text_code:         size = sizeof(mp_text_object);    break;
4593   case mp_start_clip_code:   size = sizeof(mp_clip_object);    break;
4594   case mp_start_bounds_code: size = sizeof(mp_bounds_object);  break;
4595   case mp_special_code:      size = sizeof(mp_special_object); break;
4596   default:                   size = sizeof(mp_graphic_object); break;
4597   }  
4598   p = (mp_graphic_object *)mp_xmalloc(mp,1,size);
4599   memset(p,0,size);
4600   gr_type(p) = type;
4601   return p;
4602 }
4603
4604 @ We need to keep track of several parameters from the \ps\ graphics state.
4605 @^graphics state@>
4606 This allows us to be sure that \ps\ has the correct values when they are
4607 needed without wasting time and space setting them unnecessarily.
4608
4609 @d gs_red        mp->ps->gs_state->red_field         
4610 @d gs_green      mp->ps->gs_state->green_field       
4611 @d gs_blue       mp->ps->gs_state->blue_field        
4612 @d gs_black      mp->ps->gs_state->black_field       
4613 @d gs_colormodel mp->ps->gs_state->colormodel_field  
4614 @d gs_ljoin      mp->ps->gs_state->ljoin_field       
4615 @d gs_lcap       mp->ps->gs_state->lcap_field        
4616 @d gs_adj_wx     mp->ps->gs_state->adj_wx_field      
4617 @d gs_miterlim   mp->ps->gs_state->miterlim_field    
4618 @d gs_dash_p     mp->ps->gs_state->dash_p_field      
4619 @d gs_dash_init_done mp->ps->gs_state->dash_done_field
4620 @d gs_previous   mp->ps->gs_state->previous_field    
4621 @d gs_width      mp->ps->gs_state->width_field       
4622
4623 @<Types...@>=
4624 typedef struct _gs_state {
4625   scaled red_field ;
4626   scaled green_field ; 
4627   scaled blue_field ;
4628   scaled black_field ;
4629   /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4630   quarterword colormodel_field ;
4631    /* the current colormodel */
4632   quarterword ljoin_field ;   
4633   quarterword lcap_field ;     
4634    /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4635   quarterword adj_wx_field ;
4636    /* what resolution-dependent adjustment applies to the width */
4637   scaled miterlim_field ;
4638    /* the value from the last \&{setmiterlimit} command */
4639   mp_dash_object * dash_p_field ;
4640    /* edge structure for last \&{setdash} command */
4641   boolean dash_done_field ; /* to test for initial \&{setdash} */
4642   struct _gs_state * previous_field ;
4643    /* backlink to the previous |_gs_state| structure */
4644   scaled width_field ;
4645    /* width setting or $-1$ if no \&{setlinewidth} command so far */
4646 } _gs_state;
4647
4648    
4649 @ @<Glob...@>=
4650 struct _gs_state * gs_state;
4651
4652 @ @<Set init...@>=
4653 mp->ps->gs_state=NULL;
4654
4655 @ @<Dealloc variables@>=
4656 mp_xfree(mp->ps->gs_state);
4657
4658 @ To avoid making undue assumptions about the initial graphics state, these
4659 parameters are given special values that are guaranteed not to match anything
4660 in the edge structure being shipped out.  On the other hand, the initial color
4661 should be black so that the translation of an all-black picture will have no
4662 \&{setcolor} commands.  (These would be undesirable in a font application.)
4663 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4664 to recover from a situation where we have lost track of the graphics state.
4665
4666 @c
4667 void mp_gs_unknown_graphics_state (MP mp,scaled c) ;
4668
4669
4670 @d mp_void (null+1) /* a null pointer different from |null| */
4671
4672 @c void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4673   struct _gs_state *p; /* to shift graphic states around */
4674   if ( (c==0)||(c==-1) ) {
4675     if ( mp->ps->gs_state==NULL ) {
4676       mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4677       gs_previous=NULL;
4678     } else {
4679       while ( gs_previous!=NULL ) {
4680         p = gs_previous;
4681         mp_xfree(mp->ps->gs_state);
4682         mp->ps->gs_state=p;
4683       }
4684     }
4685     gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4686     gs_colormodel=mp_uninitialized_model;
4687     gs_ljoin=3;
4688     gs_lcap=3;
4689     gs_miterlim=0;
4690     gs_dash_p=NULL;
4691     gs_dash_init_done=false;
4692     gs_width=-1;
4693   } else if ( c==1 ) {
4694     p= mp->ps->gs_state;
4695     mp->ps->gs_state =  mp_xmalloc(mp,1,sizeof(struct _gs_state));
4696     memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4697     gs_previous = p;
4698   } else if ( c==2 ) {
4699     p = gs_previous;
4700     mp_xfree(mp->ps->gs_state);
4701     mp->ps->gs_state=p;
4702   }
4703 }
4704
4705
4706 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4707 that \ps's idea of the graphics state agrees with what is stored in the object.
4708
4709 @<Declarations@>=
4710 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ;
4711
4712 @ @c 
4713 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) {
4714   /* get ready to output graphical object |p| */
4715   mp_knot *pp, *path_p; /* for list manipulation */
4716   mp_dash_object *hh;
4717   scaled wx,wy,ww; /* dimensions of pen bounding box */
4718   boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4719   integer tx,ty; /* temporaries for computing |adj_wx| */
4720   if ( gr_has_color(p) )
4721     @<Make sure \ps\ will use the right color for object~|p|@>;
4722   if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) {
4723     if (gr_type(p)==mp_fill_code) {
4724       pp = gr_pen_p((mp_fill_object *)p);
4725       path_p = gr_path_p((mp_fill_object *)p);
4726     } else {
4727       pp = gr_pen_p((mp_stroked_object *)p);
4728       path_p = gr_path_p((mp_stroked_object *)p);
4729     }
4730     if ( pp!=NULL )
4731       if ( pen_is_elliptical(pp) ) {
4732         @<Generate \ps\ code that sets the stroke width to the
4733           appropriate rounded value@>;
4734         @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4735         @<Decide whether the line cap parameter matters and set it if necessary@>;
4736         @<Set the other numeric parameters as needed for object~|p|@>;
4737       }
4738   }
4739   if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
4740 }
4741
4742 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4743 if ( gr_type(p)==mp_stroked_code ) {
4744   mp_stroked_object *ts = (mp_stroked_object *)p;
4745   if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) )
4746     if ( gs_lcap!=gr_lcap_val(ts) ) {
4747       ps_room(13);
4748       mp_ps_print_char(mp, ' ');
4749       mp_ps_print_char(mp, '0'+gr_lcap_val(ts)); 
4750       mp_ps_print_cmd(mp, " setlinecap"," lc");
4751       gs_lcap=gr_lcap_val(ts);
4752     }
4753 }
4754
4755
4756 @d set_ljoin_miterlim(p) 
4757   if ( gs_ljoin!=gr_ljoin_val(p) ) {
4758     ps_room(14);
4759     mp_ps_print_char(mp, ' ');
4760     mp_ps_print_char(mp, '0'+gr_ljoin_val(p)); 
4761     mp_ps_print_cmd(mp, " setlinejoin"," lj");
4762     gs_ljoin=gr_ljoin_val(p);
4763   }
4764   if ( gs_miterlim!=gr_miterlim_val(p) ) {
4765     ps_room(27);
4766     mp_ps_print_char(mp, ' ');
4767     mp_ps_print_scaled(mp, gr_miterlim_val(p)); 
4768     mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4769     gs_miterlim=gr_miterlim_val(p);
4770   }
4771
4772 @<Set the other numeric parameters as needed for object~|p|@>=
4773 if ( gr_type(p)==mp_stroked_code ) {
4774   mp_stroked_object *ts = (mp_stroked_object *)p;
4775   set_ljoin_miterlim(ts);
4776 } else {
4777   mp_fill_object *ts = (mp_fill_object *)p;
4778   set_ljoin_miterlim(ts);
4779 }
4780
4781
4782 @d set_color_objects(pq)
4783   object_color_model = pq->color_model_field;
4784   object_color_a = pq->color_field._a_val;
4785   object_color_b = pq->color_field._b_val;
4786   object_color_c = pq->color_field._c_val;
4787   object_color_d = pq->color_field._d_val; 
4788
4789 @<Make sure \ps\ will use the right color for object~|p|@>=
4790 {  
4791   int object_color_model;
4792   int object_color_a, object_color_b, object_color_c, object_color_d ; 
4793   if (gr_type(p) == mp_fill_code) {
4794     mp_fill_object *pq = (mp_fill_object *)p;
4795     set_color_objects(pq);
4796   } else if (gr_type(p) == mp_stroked_code) {
4797     mp_stroked_object *pq = (mp_stroked_object *)p;
4798     set_color_objects(pq);
4799   } else {
4800     mp_text_object *pq = (mp_text_object *)p;
4801     set_color_objects(pq);
4802   }
4803
4804   if ( object_color_model==mp_rgb_model) {
4805         if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)||
4806       (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) {
4807       gs_red   = object_color_a;
4808       gs_green = object_color_b;
4809       gs_blue  = object_color_c;
4810       gs_black = -1;
4811       gs_colormodel=mp_rgb_model;
4812       { ps_room(36);
4813         mp_ps_print_char(mp, ' ');
4814         mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
4815         mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
4816         mp_ps_print_scaled(mp, gs_blue);
4817         mp_ps_print_cmd(mp, " setrgbcolor", " R");
4818       }
4819     }
4820   } else if ( object_color_model==mp_cmyk_model) {
4821    if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)||
4822       (gs_blue!=object_color_c)||(gs_black!=object_color_d)||
4823       (gs_colormodel!=mp_cmyk_model) ) {
4824       gs_red   = object_color_a;
4825       gs_green = object_color_b;
4826       gs_blue  = object_color_c;
4827       gs_black = object_color_d;
4828       gs_colormodel=mp_cmyk_model;
4829       { ps_room(45);
4830         mp_ps_print_char(mp, ' ');
4831         mp_ps_print_scaled(mp, gs_red); 
4832         mp_ps_print_char(mp, ' ');
4833         mp_ps_print_scaled(mp, gs_green); 
4834         mp_ps_print_char(mp, ' ');
4835         mp_ps_print_scaled(mp, gs_blue); 
4836             mp_ps_print_char(mp, ' ');
4837         mp_ps_print_scaled(mp, gs_black);
4838         mp_ps_print_cmd(mp, " setcmykcolor"," C");
4839       }
4840     }
4841   } else if ( object_color_model==mp_grey_model ) {
4842    if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) {
4843       gs_red   = object_color_a;
4844       gs_green = -1;
4845       gs_blue  = -1;
4846       gs_black = -1;
4847       gs_colormodel=mp_grey_model;
4848       { ps_room(16);
4849         mp_ps_print_char(mp, ' ');
4850         mp_ps_print_scaled(mp, gs_red);
4851         mp_ps_print_cmd(mp, " setgray"," G");
4852       }
4853     }
4854   } else if ( object_color_model==mp_no_model ) {
4855     gs_colormodel=mp_no_model;
4856   }
4857 }
4858
4859 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4860 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4861 @:setwidth}{\&{setwidth}command@>
4862 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4863 generate \ps\ code that computes the rounded value.  For non-circular pens, the
4864 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4865 have the computed width.
4866
4867 Rounding the width to whole pixels is not likely to improve the appearance of
4868 diagonal or curved strokes, but we do it anyway for consistency.  The
4869 \&{truncate} command generated here tends to make all the strokes a little
4870 @:truncate}{\&{truncate} command@>
4871 thinner, but this is appropriate for \ps's scan-conversion rules.  Even with
4872 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4873 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4874 to compute in \ps.
4875
4876 @<Generate \ps\ code that sets the stroke width...@>=
4877 @<Set |wx| and |wy| to the width and height of the bounding box for
4878   |pen_p(p)|@>;
4879 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4880   important and set |adj_wx| and |ww| accordingly@>;
4881 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4882   if ( adj_wx ) {
4883     ps_room(13);
4884     mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
4885     mp_ps_print_cmd(mp, 
4886       " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4887   } else {
4888     if ( mp->internal[mp_procset]>0 ) {
4889       ps_room(13);
4890       mp_ps_print_char(mp, ' ');
4891       mp_ps_print_scaled(mp, ww);
4892       mp_ps_print(mp, " vlw");
4893     } else { 
4894       ps_room(15);
4895       mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
4896       mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4897     }
4898   }
4899   gs_width = ww;
4900   gs_adj_wx = adj_wx;
4901 }
4902
4903 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4904 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4905   wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4906   wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4907 } else {
4908   wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4909   wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4910 }
4911
4912 @ The path is considered ``essentially horizontal'' if its range of
4913 $y$~coordinates is less than the $y$~range |wy| for the pen.  ``Essentially
4914 vertical'' paths are detected similarly.  This code ensures that no component
4915 of the pen transformation is more that |aspect_bound*(ww+1)|.
4916
4917 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4918     more than this factor */
4919
4920 @d do_x_loc 1
4921 @d do_y_loc 2
4922
4923 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4924 tx=1; ty=1;
4925 if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound;
4926 else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound;
4927 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
4928 else { ww=wx; adj_wx=true;  }
4929
4930 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4931 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4932 allowable range for $x$ or~$y$.  We do not need and cannot afford a full
4933 bounding-box computation.
4934
4935 @<Declarations@>=
4936 boolean mp_gr_coord_rangeOK (mp_knot *h, 
4937                           small_number  zoff, scaled dz);
4938
4939 @ @c
4940 boolean mp_gr_coord_rangeOK (mp_knot *h, 
4941                           small_number  zoff, scaled dz) {
4942   mp_knot *p; /* for scanning the path form |h| */
4943   scaled zlo,zhi; /* coordinate range so far */
4944   scaled z; /* coordinate currently being tested */
4945   if (zoff==do_x_loc) {
4946     zlo=gr_x_coord(h);
4947     zhi=zlo;
4948     p=h;
4949     while ( gr_right_type(p)!=mp_endpoint ) {
4950       z=gr_right_x(p);
4951       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4952       p=gr_next_knot(p);  z=gr_left_x(p);
4953       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4954       z=gr_x_coord(p);
4955       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4956       if ( p==h ) break;
4957     }
4958   } else {
4959     zlo=gr_y_coord(h);
4960     zhi=zlo;
4961     p=h;
4962     while ( gr_right_type(p)!=mp_endpoint ) {
4963       z=gr_right_y(p);
4964       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4965       p=gr_next_knot(p); z=gr_left_y(p);
4966       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4967       z=gr_y_coord(p);
4968       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4969       if ( p==h ) break;
4970     }
4971   }
4972   return true;
4973 }
4974
4975 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4976 if ( z<zlo ) zlo=z;
4977 else if ( z>zhi ) zhi=z;
4978 if ( zhi-zlo>dz ) return false
4979
4980 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4981 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4982 @:stroke}{\&{stroke} command@>
4983 @:fill}{\&{fill} command@>
4984 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4985 a reference.
4986
4987 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4988 if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) {
4989   hh=NULL;
4990 } else { 
4991   hh=gr_dash_p(p);
4992 }
4993 if ( hh==NULL ) {
4994   if ( gs_dash_p!=NULL || gs_dash_init_done == false) {
4995     mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4996     gs_dash_p=NULL;
4997         gs_dash_init_done=true;
4998   }
4999 } else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) {
5000   @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
5001 }
5002
5003 @ @<Declarations@>=
5004 scaled mp_gr_get_pen_scale (MP mp, mp_knot *p) ;
5005
5006
5007 @ @c
5008 scaled mp_gr_get_pen_scale (MP mp, mp_knot *p) { 
5009   return mp_sqrt_det(mp, 
5010     gr_left_x(p)-gr_x_coord(p), gr_right_x(p)-gr_x_coord(p),
5011     gr_left_y(p)-gr_y_coord(p), gr_right_y(p)-gr_y_coord(p));
5012 }
5013
5014
5015 @ The original code had a check here to ensure that the result from
5016 |mp_take_scaled| did not go out of bounds.
5017
5018 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5019 { gs_dash_p=hh;
5020   if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array_field==NULL)) {
5021     mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5022   } else { 
5023         int i;
5024     ps_room(28);
5025     mp_ps_print(mp, " [");
5026     for (i=0; *(hh->array_field+i) != -1;i++) {
5027       ps_room(13);
5028       mp_ps_print_scaled(mp, *(hh->array_field+i)); 
5029           mp_ps_print_char(mp, ' ')     ;
5030     }
5031     ps_room(22);
5032     mp_ps_print(mp, "] ");
5033     mp_ps_print_scaled(mp, hh->offset_field);
5034     mp_ps_print_cmd(mp, " setdash"," sd");
5035   }
5036 }
5037
5038 @ @<Declarations@>=
5039 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ;
5040
5041 @ This function test if |h| and |hh| represent the same dash pattern.
5042
5043 @c
5044 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) {
5045   boolean ret=false;
5046   int i = 0; 
5047   if ( h==hh ) ret=true;
5048   else if ( (h==NULL)||(hh==NULL) ) ret=false;
5049   else if ( h->offset_field!=hh->offset_field ) ret=false;
5050   else if ( h->array_field == hh->array_field) ret=true;
5051   else if ( h->array_field == NULL || hh->array_field == NULL) ret=false;
5052   else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5053   return ret;
5054 }
5055
5056 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5057 {
5058   while (*(h->array_field+i)!=-1 && 
5059              *(hh->array_field+i)!=-1 &&
5060              *(h->array_field+i) == *(hh->array_field+i)) i++;
5061   if (i>0) {
5062     if (*(h->array_field+(i))==-1 && *(hh->array_field+(i)) == -1) 
5063       ret=true;
5064   }
5065 }
5066
5067 @ When stroking a path with an elliptical pen, it is necessary to transform
5068 the coordinate system so that a unit circular pen will have the desired shape.
5069 To keep this transformation local, we enclose it in a
5070 $$\&{gsave}\ldots\&{grestore}$$
5071 block. Any translation component must be applied to the path being stroked
5072 while the rest of the transformation must apply only to the pen.
5073 If |fill_also=true|, the path is to be filled as well as stroked so we must
5074 insert commands to do this after giving the path.
5075
5076 @<Declarations@>=
5077 void mp_gr_stroke_ellipse (MP mp,  mp_graphic_object *h, boolean fill_also) ;
5078
5079
5080 @c void mp_gr_stroke_ellipse (MP mp,  mp_graphic_object *h, boolean fill_also) {
5081   /* generate an elliptical pen stroke from object |h| */
5082   scaled txx,txy,tyx,tyy; /* transformation parameters */
5083   mp_knot *p; /* the pen to stroke with */
5084   scaled d1,det; /* for tweaking transformation parameters */
5085   integer s; /* also for tweaking transformation paramters */
5086   boolean transformed; /* keeps track of whether gsave/grestore are needed */
5087   transformed=false;
5088   @<Use |pen_p(h)| to set the transformation parameters and give the initial
5089     translation@>;
5090   @<Tweak the transformation parameters so the transformation is nonsingular@>;
5091   if (gr_type(h)==mp_fill_code) {
5092     mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h));
5093   } else {
5094     mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h));
5095   }
5096   if ( mp->internal[mp_procset]==0 ) {
5097     if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5098     @<Issue \ps\ commands to transform the coordinate system@>;
5099     mp_ps_print(mp, " stroke");
5100     if ( transformed ) mp_ps_print(mp, " grestore");
5101   } else {
5102     if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5103     if ( (txy!=0)||(tyx!=0) ) {
5104       mp_ps_print(mp, " [");
5105       mp_ps_pair_out(mp, txx,tyx);
5106       mp_ps_pair_out(mp, txy,tyy);
5107       mp_ps_print(mp, "0 0] t");
5108     } else if ((txx!=unity)||(tyy!=unity) )  {
5109       mp_ps_pair_out(mp,txx,tyy);
5110       mp_ps_print(mp, " s");
5111     };
5112     mp_ps_print(mp, " S");
5113     if ( transformed ) mp_ps_print(mp, " Q");
5114   }
5115   mp_ps_print_ln(mp);
5116 }
5117
5118 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5119 if (gr_type(h)==mp_fill_code) {
5120   p=gr_pen_p((mp_fill_object *)h);
5121 } else {
5122   p=gr_pen_p((mp_stroked_object *)h);
5123 }
5124 txx=gr_left_x(p);
5125 tyx=gr_left_y(p);
5126 txy=gr_right_x(p);
5127 tyy=gr_right_y(p);
5128 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5129   mp_ps_print_nl(mp, ""); 
5130   mp_ps_print_cmd(mp, "gsave ","q ");
5131   mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5132   mp_ps_print(mp, "translate ");
5133   txx-=gr_x_coord(p);
5134   tyx-=gr_y_coord(p);
5135   txy-=gr_x_coord(p);
5136   tyy-=gr_y_coord(p);
5137   transformed=true;
5138 } else {
5139   mp_ps_print_nl(mp, "");
5140 }
5141 @<Adjust the transformation to account for |gs_width| and output the
5142   initial \&{gsave} if |transformed| should be |true|@>
5143
5144 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5145 if ( gs_width!=unity ) {
5146   if ( gs_width==0 ) { 
5147     txx=unity; tyy=unity;
5148   } else { 
5149     txx=mp_make_scaled(mp, txx,gs_width);
5150     txy=mp_make_scaled(mp, txy,gs_width);
5151     tyx=mp_make_scaled(mp, tyx,gs_width);
5152     tyy=mp_make_scaled(mp, tyy,gs_width);
5153   };
5154 }
5155 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5156   if ( (! transformed) ){ 
5157     mp_ps_print_cmd(mp, "gsave ","q ");
5158     transformed=true;
5159   }
5160 }
5161
5162 @ @<Issue \ps\ commands to transform the coordinate system@>=
5163 if ( (txy!=0)||(tyx!=0) ){ 
5164   mp_ps_print_ln(mp);
5165   mp_ps_print_char(mp, '[');
5166   mp_ps_pair_out(mp, txx,tyx);
5167   mp_ps_pair_out(mp, txy,tyy);
5168   mp_ps_print(mp, "0 0] concat");
5169 } else if ( (txx!=unity)||(tyy!=unity) ){ 
5170   mp_ps_print_ln(mp);
5171   mp_ps_pair_out(mp, txx,tyy);
5172   mp_ps_print(mp, "scale");
5173 }
5174
5175 @ The \ps\ interpreter will probably abort if it encounters a singular
5176 transformation matrix.  The determinant must be large enough to ensure that
5177 the printed representation will be nonsingular.  Since the printed
5178 representation is always within $2^{-17}$ of the internal |scaled| value, the
5179 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5180 the magnitudes of |txx/65536|, |txy/65536|, etc.
5181
5182 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5183 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5184
5185 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5186 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5187 d1=4*aspect_bound+1;
5188 if ( abs(det)<d1 ) { 
5189   if ( det>=0 ) { d1=d1-det; s=1;  }
5190   else { d1=-d1-det; s=-1;  };
5191   d1=d1*unity;
5192   if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5193     if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5194     else txx=txx+(d1+s*abs(tyy)) / tyy;
5195   } else {
5196     if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5197     else txy=txy+(d1+s*abs(tyx)) / tyx;
5198   }
5199 }
5200
5201 @ Here is a simple routine that just fills a cycle.
5202
5203 @<Declarations@>=
5204 void mp_gr_ps_fill_out (MP mp, mp_knot *p);
5205
5206 @ @c
5207 void mp_gr_ps_fill_out (MP mp, mp_knot *p) { /* fill cyclic path~|p| */
5208   mp_gr_ps_path_out(mp, p);
5209   mp_ps_print_cmd(mp, " fill"," F");
5210   mp_ps_print_ln(mp);
5211 }
5212
5213 @ A text node may specify an arbitrary transformation but the usual case
5214 involves only shifting, scaling, and occasionally rotation.  The purpose
5215 of |choose_scale| is to select a scale factor so that the remaining
5216 transformation is as ``nice'' as possible.  The definition of ``nice''
5217 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5218 nice because they work out well for bitmap fonts.  The code here selects
5219 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5220 non-shifting part of the transformation matrix.  It is careful to avoid
5221 additions that might cause undetected overflow.
5222
5223 @<Declarations@>=
5224 scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) ;
5225
5226 @ @c scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) {
5227   /* |p| should point to a text node */
5228   scaled a,b,c,d,ad,bc; /* temporary values */
5229   a=gr_txx_val(p);
5230   b=gr_txy_val(p);
5231   c=gr_tyx_val(p);
5232   d=gr_tyy_val(p);
5233   if ( a<0 ) negate(a);
5234   if ( b<0 ) negate(b);
5235   if ( c<0 ) negate(c);
5236   if ( d<0 ) negate(d);
5237   ad=half(a-d);
5238   bc=half(b-c);
5239   return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5240 }
5241
5242 @ The potential overflow here is caused by the fact the returned value
5243 has to fit in a |name_type|, which is a quarterword. 
5244
5245 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
5246
5247 @<Declarations@>=
5248 quarterword mp_size_index (MP mp, font_number f, scaled s) ;
5249
5250 @ @c
5251 quarterword mp_size_index (MP mp, font_number f, scaled s) {
5252   pointer p,q; /* the previous and current font size nodes */
5253   quarterword i; /* the size index for |q| */
5254   q=mp->font_sizes[f];
5255   i=0;
5256   while ( q!=null ) {
5257     if ( abs(s-sc_factor(q))<=fscale_tolerance ) 
5258       return i;
5259     else 
5260       { p=q; q=link(q); incr(i); };
5261     if ( i==max_quarterword )
5262       mp_overflow(mp, "sizes per font",max_quarterword);
5263 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
5264   }
5265   q=mp_get_node(mp, font_size_size);
5266   sc_factor(q)=s;
5267   if ( i==0 ) mp->font_sizes[f]=q;  else link(p)=q;
5268   return i;
5269 }
5270
5271 @ @<Declarations@>=
5272 scaled mp_indexed_size (MP mp,font_number f, quarterword j);
5273
5274 @ @c
5275 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
5276   pointer p; /* a font size node */
5277   quarterword i; /* the size index for |p| */
5278   p=mp->font_sizes[f];
5279   i=0;
5280   if ( p==null ) mp_confusion(mp, "size");
5281   while ( (i!=j) ) { 
5282     incr(i); p=link(p);
5283     if ( p==null ) mp_confusion(mp, "size");
5284   }
5285   return sc_factor(p);
5286 }
5287
5288 @ @<Declarations@>=
5289 void mp_clear_sizes (MP mp) ;
5290
5291 @ @c void mp_clear_sizes (MP mp) {
5292   font_number f;  /* the font whose size list is being cleared */
5293   pointer p;  /* current font size nodes */
5294   for (f=null_font+1;f<=mp->last_fnum;f++) {
5295     while ( mp->font_sizes[f]!=null ) {
5296       p=mp->font_sizes[f];
5297       mp->font_sizes[f]=link(p);
5298       mp_free_node(mp, p,font_size_size);
5299     }
5300   }
5301 }
5302
5303 @ A text node may specify an arbitrary transformation but the usual case
5304 involves only shifting, scaling, and occasionally rotation.  The purpose
5305 of |choose_scale| is to select a scale factor so that the remaining
5306 transformation is as ``nice'' as possible.  The definition of ``nice''
5307 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5308 nice because they work out well for bitmap fonts.  The code here selects
5309 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5310 non-shifting part of the transformation matrix.  It is careful to avoid
5311 additions that might cause undetected overflow.
5312
5313 @<Declare the \ps\ output procedures@>=
5314 scaled mp_choose_scale (MP mp, mp_graphic_object *p) ;
5315
5316 @ @c scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
5317   /* |p| should point to a text node */
5318   scaled a,b,c,d,ad,bc; /* temporary values */
5319   a=gr_txx_val(p);
5320   b=gr_txy_val(p);
5321   c=gr_tyx_val(p);
5322   d=gr_tyy_val(p);
5323   if ( (a<0) ) negate(a);
5324   if ( (b<0) ) negate(b);
5325   if ( (c<0) ) negate(c);
5326   if ( (d<0) ) negate(d);
5327   ad=half(a-d);
5328   bc=half(b-c);
5329   return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5330 }
5331
5332 @ There may be many sizes of one font and we need to keep track of the
5333 characters used for each size.  This is done by keeping a linked list of
5334 sizes for each font with a counter in each text node giving the appropriate
5335 position in the size list for its font.
5336
5337 @d font_size_size 2 /* size of a font size node */
5338
5339
5340 @ @<Declarations@>=
5341 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size);
5342
5343 @ @c
5344 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) {
5345   mp_graphic_object * p;
5346   p=h->body;
5347   while ( p!= NULL ) {
5348     if ( gr_type(p)==mp_text_code ) {
5349       if ( gr_font_n(p)!=null_font ) { 
5350         if ( gr_name_type(p)==next_size )
5351           mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p));
5352       }
5353     }
5354     p=gr_link(p);
5355   }
5356 }
5357
5358 @ @<Unmark all marked characters@>=
5359 for (f=null_font+1;f<=mp->last_fnum;f++) {
5360   if ( mp->font_sizes[f]!=null ) {
5361     mp_unmark_font(mp, f);
5362     mp->font_sizes[f]=null;
5363   }
5364 }
5365
5366 @ @<Scan all the text nodes and mark the used ...@>=
5367 p=hh->body;
5368 while ( p!=null ) {
5369   if ( gr_type(p)==mp_text_code ) {
5370     f = gr_font_n(p);
5371     if (f!=null_font ) {
5372       switch (prologues) {
5373       case 2:
5374       case 3:
5375         mp->font_sizes[f] = mp_void;
5376         mp_mark_string_chars(mp, f, gr_text_p(p));
5377             if (mp_has_fm_entry(mp,f,NULL) ) {
5378           if (mp->font_enc_name[f]==NULL )
5379             mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
5380           mp->font_ps_name[f] = mp_fm_font_name(mp,f);
5381         }
5382         break;
5383       case 1:
5384         mp->font_sizes[f]=mp_void;
5385         break;
5386       default: 
5387         gr_name_type(p)=mp_size_index(mp, f,mp_choose_scale(mp, p));
5388         if ( gr_name_type(p)==0 )
5389           mp_mark_string_chars(mp, f, gr_text_p(p));
5390       }
5391     }
5392   }
5393   p=gr_link(p);
5394 }
5395
5396
5397
5398 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5399
5400 @<Exported function ...@>=
5401 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) ;
5402
5403 @ @c 
5404 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) {
5405   mp_graphic_object *p;
5406   scaled ds,scf; /* design size and scale factor for a text node */
5407   font_number f; /* for loops over fonts while (un)marking characters */
5408   boolean transformed; /* is the coordinate system being transformed? */
5409   MP mp = hh->_parent;
5410   if (standalone) {
5411      jmp_buf buf;
5412      mp->jump_buf = &buf;
5413      if (setjmp(*(mp->jump_buf)))
5414        return 0;
5415   }
5416   if (mp->history >= mp_fatal_error_stop ) return 1;
5417   if (prologues<0) 
5418         prologues = (mp->internal[mp_prologues]>>16);
5419   if (procset<0) 
5420         procset = (mp->internal[mp_procset]>>16);
5421   mp_open_output_file(mp);
5422   mp_print_initial_comment(mp, hh, prologues);
5423   p = hh->body;
5424   @<Unmark all marked characters@>;
5425   if ( prologues==2 || prologues==3 ) {
5426     mp_reload_encodings(mp);
5427   }
5428   @<Scan all the text nodes and mark the used characters@>;
5429   if ( prologues==2 || prologues==3 ) {
5430     mp_print_improved_prologue(mp, hh, prologues, procset);
5431   } else {
5432     mp_print_prologue(mp, hh, prologues, procset);
5433   }
5434   mp_gs_unknown_graphics_state(mp, 0);
5435   p = hh->body;
5436   while ( p!=NULL ) { 
5437     if ( gr_has_color(p) ) {
5438       @<Write |pre_script| of |p|@>;
5439     }
5440     mp_gr_fix_graphics_state(mp, p);
5441     switch (gr_type(p)) {
5442     case mp_fill_code: 
5443       if ( gr_pen_p((mp_fill_object *)p)==NULL ) {
5444         mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5445       } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) )  {
5446         mp_gr_stroke_ellipse(mp, p,true);
5447       } else { 
5448         mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5449         mp_gr_ps_fill_out(mp, gr_htap_p(p));
5450       }
5451       if ( gr_post_script((mp_fill_object *)p)!=NULL ) {
5452          mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p)); 
5453              mp_ps_print_ln(mp);
5454       }
5455       break;
5456     case mp_stroked_code:
5457       if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) ) 
5458             mp_gr_stroke_ellipse(mp, p,false);
5459       else { 
5460         mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p));
5461       }
5462       if ( gr_post_script((mp_stroked_object *)p)!=NULL ) {
5463         mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p)); 
5464         mp_ps_print_ln(mp);
5465       }
5466       break;
5467     case mp_text_code: 
5468       if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5469         if ( prologues>0 )
5470           scf=mp_gr_choose_scale(mp, p);
5471         else 
5472           scf=mp_indexed_size(mp, gr_font_n(p), gr_name_type(p));
5473         @<Shift or transform as necessary before outputting text node~|p| at scale
5474           factor~|scf|; set |transformed:=true| if the original transformation must
5475           be restored@>;
5476         mp_ps_string_out(mp, gr_text_p(p));
5477         mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5478         @<Print the size information and \ps\ commands for text node~|p|@>;
5479         mp_ps_print_ln(mp);
5480       }
5481       if ( gr_post_script((mp_text_object *)p)!=NULL ) {
5482         mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp);
5483       }
5484       break;
5485     case mp_start_clip_code: 
5486       mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5487       mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p));
5488       mp_ps_print_cmd(mp, " clip"," W");
5489       mp_ps_print_ln(mp);
5490       if ( mp->internal[mp_restore_clip_color]>0 )
5491         mp_gs_unknown_graphics_state(mp, 1);
5492       break;
5493     case mp_stop_clip_code: 
5494       mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5495       mp_ps_print_ln(mp);
5496       if ( mp->internal[mp_restore_clip_color]>0 )
5497         mp_gs_unknown_graphics_state(mp, 2);
5498       else
5499         mp_gs_unknown_graphics_state(mp, -1);
5500       break;
5501     case mp_start_bounds_code:
5502     case mp_stop_bounds_code:
5503           break;
5504     case mp_special_code:  
5505       {
5506         mp_special_object *ps = (mp_special_object *)p;
5507         mp_ps_print_nl (mp, gr_pre_script(ps)); 
5508             mp_ps_print_ln (mp);
5509       }
5510       break;
5511     } /* all cases are enumerated */
5512     p=gr_link(p);
5513   }
5514   mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
5515   mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
5516   (mp->close_file)(mp,mp->ps_file);
5517   if ( prologues<=0 ) 
5518     mp_clear_sizes(mp);
5519   return 1;
5520 }
5521
5522 @ @(psout.h@>=
5523 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) ;
5524
5525 @ @c
5526 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) {
5527   return mp_gr_ship_out (hh, prologues, procset, true);
5528 }
5529
5530
5531
5532
5533
5534
5535 @d do_write_prescript(a,b) {
5536   if ( (gr_pre_script((b *)a))!=NULL ) {
5537     mp_ps_print_nl (mp, gr_pre_script((b *)a)); 
5538     mp_ps_print_ln(mp);
5539   }
5540 }
5541
5542 @<Write |pre_script| of |p|@>=
5543 {
5544   if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); }
5545   else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); }
5546   else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); }
5547 }
5548
5549 @ The envelope of a cyclic path~|q| could be computed by calling
5550 |make_envelope| once for |q| and once for its reversal.  We don't do this
5551 because it would fail color regions that are covered by the pen regardless
5552 of where it is placed on~|q|.
5553
5554 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5555 if ( gr_left_type(q)!=mp_endpoint ) { 
5556   gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5557   gr_right_type(q)=mp_endpoint;
5558   q=gr_next_knot(q);
5559   t=1;
5560 }
5561
5562 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5563 ps_room(18);
5564 mp_ps_print_char(mp, ' ');
5565 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5566 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5567 mp_ps_print(mp, " fshow");
5568 if ( transformed ) 
5569    mp_ps_print_cmd(mp, " grestore"," Q")
5570
5571
5572
5573 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5574 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5575             (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5576 if ( transformed ) {
5577   mp_ps_print_cmd(mp, "gsave [", "q [");
5578   mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5579                      mp_make_scaled(mp, gr_tyx_val(p),scf));
5580   mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5581                      mp_make_scaled(mp, gr_tyy_val(p),scf));
5582   mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5583   mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5584 } else { 
5585   mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5586   mp_ps_print_cmd(mp, "moveto","m");
5587 }
5588 mp_ps_print_ln(mp)
5589
5590 @ @(psout.h@>=
5591 void mp_gr_toss_objects ( mp_edge_object *hh) ;
5592 void mp_gr_toss_object (mp_graphic_object *p) ;
5593
5594 @ @c
5595 void mp_gr_toss_object (mp_graphic_object *p) {
5596     mp_fill_object *tf;
5597     mp_stroked_object *ts;
5598     mp_text_object *tt;
5599     switch (gr_type(p)) {       
5600     case mp_fill_code: 
5601       tf = (mp_fill_object *)p;
5602       mp_xfree(gr_pre_script(tf));
5603       mp_xfree(gr_post_script(tf));
5604       mp_gr_toss_knot_list(mp,gr_pen_p(tf));
5605       mp_gr_toss_knot_list(mp,gr_path_p(tf));
5606       mp_gr_toss_knot_list(mp,gr_htap_p(p));
5607           break;
5608     case mp_stroked_code:
5609       ts = (mp_stroked_object *)p;
5610       mp_xfree(gr_pre_script(ts));
5611       mp_xfree(gr_post_script(ts));
5612       mp_gr_toss_knot_list(mp,gr_pen_p(ts));
5613       mp_gr_toss_knot_list(mp,gr_path_p(ts));
5614       if (gr_dash_p(p)!=NULL) 
5615         mp_gr_toss_dashes   (mp,gr_dash_p(p));
5616       break;
5617     case mp_text_code: 
5618       tt = (mp_text_object *)p;
5619       mp_xfree(gr_pre_script(tt));
5620       mp_xfree(gr_post_script(tt));
5621       mp_xfree(gr_text_p(p));
5622       mp_xfree(gr_font_name(p));
5623       break;
5624     case mp_start_clip_code: 
5625       mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p));
5626       break;
5627     case mp_start_bounds_code:
5628       mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p));
5629       break;
5630     case mp_stop_clip_code: 
5631     case mp_stop_bounds_code:
5632           break;
5633     case mp_special_code: 
5634       mp_xfree(gr_pre_script((mp_special_object *)p));
5635           break;
5636     } /* all cases are enumerated */
5637     mp_xfree(p);
5638 }
5639
5640
5641 @ @c
5642 void mp_gr_toss_objects (mp_edge_object *hh) {
5643   mp_graphic_object *p, *q;
5644   p = hh->body;
5645   while ( p!=NULL ) { 
5646     q = gr_link(p);
5647     mp_gr_toss_object(p);
5648     p=q;
5649   }
5650   mp_xfree(hh);
5651 }
5652
5653 @ @(psout.h@>=
5654 mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ;
5655
5656 @ @c
5657 mp_graphic_object * 
5658 mp_gr_copy_object (MP mp, mp_graphic_object *p) {
5659   mp_fill_object *tf;
5660   mp_stroked_object *ts;
5661   mp_text_object *tt;
5662   mp_clip_object *tc;
5663   mp_bounds_object *tb;
5664   mp_special_object *tp;
5665   mp_graphic_object *q = NULL;
5666   switch (gr_type(p)) { 
5667   case mp_fill_code: 
5668     tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code);
5669     gr_pre_script(tf)  = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p));
5670     gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p));
5671     gr_path_p(tf)      = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p));
5672     gr_htap_p(tf)      = mp_gr_copy_path(mp,gr_htap_p(p));
5673     gr_pen_p(tf)       = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p));
5674     q = (mp_graphic_object *)tf;
5675     break;
5676   case mp_stroked_code:
5677     ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code);
5678     gr_pre_script(ts)  = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p));
5679     gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p));
5680     gr_path_p(ts)      = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p));
5681     gr_pen_p(ts)       = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p));
5682     gr_dash_p(ts)      = mp_gr_copy_dashes(mp,gr_dash_p(p));
5683     q = (mp_graphic_object *)ts;
5684     break;
5685   case mp_text_code: 
5686     tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code);
5687     gr_pre_script(tt)  = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p));
5688     gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p));
5689     gr_text_p(tt)      = mp_xstrdup(mp, gr_text_p(p));
5690     gr_font_name(tt)   = mp_xstrdup(mp, gr_font_name(p));
5691     q = (mp_graphic_object *)tt;
5692     break;
5693   case mp_start_clip_code: 
5694     tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code);
5695     gr_path_p(tc)      = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p));
5696     q = (mp_graphic_object *)tc;
5697     break;
5698   case mp_start_bounds_code:
5699     tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code);
5700     gr_path_p(tb)      = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p));
5701     q = (mp_graphic_object *)tb;
5702     break;
5703   case mp_special_code: 
5704     tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code);
5705     gr_pre_script(tp)  = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p));
5706     q = (mp_graphic_object *)tp;
5707     break;
5708   case mp_stop_clip_code: 
5709     q = mp_new_graphic_object(mp, mp_stop_clip_code);
5710     break;
5711   case mp_stop_bounds_code:
5712     q = mp_new_graphic_object(mp, mp_stop_bounds_code);
5713     break;
5714   } /* all cases are enumerated */
5715   return q;
5716 }
5717