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