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