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