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