fix the initial degenerate cubic case in a better manner
[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, pointer h, 
4165                               scaled minx, scaled miny, scaled maxx, scaled maxy);
4166
4167 @ @c
4168 void mp_print_initial_comment(MP mp, pointer h, 
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   q=p;
4329   do {  
4330     r=gr_next_knot(q); 
4331     mp_xfree(q); q=r;
4332   } while (q!=p);
4333 }
4334
4335
4336
4337 @ @c
4338 void mp_gr_ps_path_out (MP mp, struct mp_knot *h) {
4339   struct mp_knot *p, *q; /* for scanning the path */
4340   scaled d; /* a temporary value */
4341   boolean curved; /* |true| unless the cubic is almost straight */
4342   ps_room(40);
4343   mp_ps_print_cmd(mp, "newpath ","n ");
4344   mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4345   mp_ps_print_cmd(mp, "moveto","m");
4346   p=h;
4347   do {  
4348     if ( gr_right_type(p)==mp_endpoint ) { 
4349       if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4350       return;
4351     }
4352     q=gr_next_knot(p);
4353     @<Start a new line and print the \ps\ commands for the curve from
4354       |p| to~|q|@>;
4355     p=q;
4356   } while (p!=h);
4357   mp_ps_print_cmd(mp, " closepath"," p");
4358 }
4359
4360 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4361 curved=true;
4362 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4363 mp_print_ln(mp);
4364 if ( curved ){ 
4365   mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4366   mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4367   mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4368   mp_ps_print_cmd(mp, "curveto","c");
4369 } else if ( q!=h ){ 
4370   mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4371   mp_ps_print_cmd(mp, "lineto","l");
4372 }
4373
4374 @ Two types of straight lines come up often in \MP\ paths:
4375 cubics with zero initial and final velocity as created by |make_path| or
4376 |make_envelope|, and cubics with control points uniformly spaced on a line
4377 as created by |make_choices|.
4378
4379 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4380
4381 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4382 if ( gr_right_x(p)==gr_x_coord(p) )
4383   if ( gr_right_y(p)==gr_y_coord(p) )
4384     if ( gr_left_x(q)==gr_x_coord(q) )
4385       if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4386 d=gr_left_x(q)-gr_right_x(p);
4387 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4388   if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4389     { d=gr_left_y(q)-gr_right_y(p);
4390     if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4391       if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4392     }
4393
4394 @ The colored objects use a union to express the color parts:
4395
4396 @<Types...@>=
4397 typedef union {
4398   struct {
4399     scaled red_val, green_val, blue_val;
4400   } rgb;
4401   struct {
4402     scaled cyan_val, magenta_val, yellow_val, black_val;
4403   } cmyk;
4404   struct {
4405     scaled grey_val;
4406   } grey ;
4407 } mp_color;
4408
4409 @
4410  
4411 @d gr_start_x(A)    (A)->start_x_field
4412 @d gr_stop_x(A)     (A)->stop_x_field
4413 @d gr_dash_link(A)  (A)->next_field
4414
4415 @d gr_dash_list(A)  (A)->list_field
4416 @d gr_dash_y(A)     (A)->y_field
4417
4418 @<Types...@>=
4419 typedef struct mp_dash_item {
4420   scaled start_x_field;
4421   scaled stop_x_field;
4422   struct mp_dash_item *next_field;
4423 } mp_dash_item ;
4424 typedef struct mp_dash_list {
4425   struct mp_dash_item *list_field;
4426   scaled y_field;
4427 } mp_dash_list ;
4428
4429
4430 @ Now for outputting the actual graphic objects. First, set up some 
4431 structures and access macros.
4432
4433 @d gr_type(A)         (A)->_type_field
4434 @d gr_link(A)         (A)->_link_field
4435 @d gr_name_type(A)    (A)->name_type_field
4436 @d gr_color_model(A)  (A)->color_model_field
4437 @d gr_red_val(A)      (A)->color_field.rgb.red_val
4438 @d gr_green_val(A)    (A)->color_field.rgb.green_val
4439 @d gr_blue_val(A)     (A)->color_field.rgb.blue_val
4440 @d gr_cyan_val(A)     (A)->color_field.cmyk.cyan_val
4441 @d gr_magenta_val(A)  (A)->color_field.cmyk.magenta_val
4442 @d gr_yellow_val(A)   (A)->color_field.cmyk.yellow_val
4443 @d gr_black_val(A)    (A)->color_field.cmyk.black_val
4444 @d gr_grey_val(A)     (A)->color_field.grey.grey_val
4445 @d gr_path_p(A)       (A)->path_p_field 
4446 @d gr_htap_p(A)       (A)->htap_p_field 
4447 @d gr_pen_p(A)        (A)->pen_p_field 
4448 @d gr_ljoin_val(A)    (A)->ljoin_field
4449 @d gr_lcap_val(A)     (A)->lcap_field
4450 @d gr_dash_scale(A)   (A)->dash_scale_field
4451 @d gr_miterlim_val(A) (A)->miterlim_field
4452 @d gr_pre_script(A)   (A)->pre_script_field
4453 @d gr_post_script(A)  (A)->post_script_field
4454 @d gr_dash_p(A)       (A)->dash_p_field
4455 @d gr_text_p(A)       (A)->text_p_field 
4456 @d gr_font_n(A)       (A)->font_n_field 
4457 @d gr_width_val(A)    (A)->width_field
4458 @d gr_height_val(A)   (A)->height_field
4459 @d gr_depth_val(A)    (A)->depth_field
4460 @d gr_tx_val(A)       (A)->tx_field
4461 @d gr_ty_val(A)       (A)->ty_field
4462 @d gr_txx_val(A)      (A)->txx_field
4463 @d gr_txy_val(A)      (A)->txy_field
4464 @d gr_tyx_val(A)      (A)->tyx_field
4465 @d gr_tyy_val(A)      (A)->tyy_field
4466
4467 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4468
4469 @<Types...@>=
4470 typedef struct mp_graphic_object {
4471   halfword _type_field;
4472   quarterword name_type_field;
4473   struct mp_graphic_object * _link_field;
4474   struct mp_knot * path_p_field;
4475   struct mp_knot * htap_p_field;
4476   struct mp_knot * pen_p_field;
4477   quarterword color_model_field;
4478   mp_color color_field;
4479   quarterword ljoin_field ;   
4480   quarterword lcap_field ;   
4481   scaled miterlim_field ;
4482   scaled dash_scale_field ;
4483   char *pre_script_field;
4484   char *post_script_field;
4485   struct mp_dash_list *dash_p_field;
4486   char *text_p_field;
4487   font_number font_n_field ;   
4488   scaled width_field ;
4489   scaled height_field ;
4490   scaled depth_field ;
4491   scaled tx_field ;
4492   scaled ty_field ;
4493   scaled txx_field ;
4494   scaled txy_field ;
4495   scaled tyx_field ;
4496   scaled tyy_field ;
4497 } mp_graphic_object;
4498
4499
4500 @ We need to keep track of several parameters from the \ps\ graphics state.
4501 @^graphics state@>
4502 This allows us to be sure that \ps\ has the correct values when they are
4503 needed without wasting time and space setting them unnecessarily.
4504
4505 @d gs_red        mp->ps->gs_state->red_field         
4506 @d gs_green      mp->ps->gs_state->green_field       
4507 @d gs_blue       mp->ps->gs_state->blue_field        
4508 @d gs_black      mp->ps->gs_state->black_field       
4509 @d gs_colormodel mp->ps->gs_state->colormodel_field  
4510 @d gs_ljoin      mp->ps->gs_state->ljoin_field       
4511 @d gs_lcap       mp->ps->gs_state->lcap_field        
4512 @d gs_adj_wx     mp->ps->gs_state->adj_wx_field      
4513 @d gs_miterlim   mp->ps->gs_state->miterlim_field    
4514 @d gs_dash_p     mp->ps->gs_state->dash_p_field      
4515 @d gs_previous   mp->ps->gs_state->previous_field    
4516 @d gs_dash_sc    mp->ps->gs_state->dash_sc_field     
4517 @d gs_width      mp->ps->gs_state->width_field       
4518
4519 @<Types...@>=
4520 typedef struct _gs_state {
4521   scaled red_field ;
4522   scaled green_field ; 
4523   scaled blue_field ;
4524   scaled black_field ;
4525   /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4526   quarterword colormodel_field ;
4527    /* the current colormodel */
4528   quarterword ljoin_field ;   
4529   quarterword lcap_field ;     
4530    /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4531   quarterword adj_wx_field ;
4532    /* what resolution-dependent adjustment applies to the width */
4533   scaled miterlim_field ;
4534    /* the value from the last \&{setmiterlimit} command */
4535   struct mp_dash_list * dash_p_field ;
4536    /* edge structure for last \&{setdash} command */
4537   struct _gs_state * previous_field ;
4538    /* backlink to the previous |_gs_state| structure */
4539   scaled dash_sc_field ;
4540    /* scale factor used with |gs_dash_p| */
4541   scaled width_field ;
4542    /* width setting or $-1$ if no \&{setlinewidth} command so far */
4543 } _gs_state;
4544
4545    
4546 @ @<Glob...@>=
4547 struct _gs_state * gs_state;
4548
4549 @ @<Set init...@>=
4550 mp->ps->gs_state=NULL;
4551
4552 @ To avoid making undue assumptions about the initial graphics state, these
4553 parameters are given special values that are guaranteed not to match anything
4554 in the edge structure being shipped out.  On the other hand, the initial color
4555 should be black so that the translation of an all-black picture will have no
4556 \&{setcolor} commands.  (These would be undesirable in a font application.)
4557 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4558 to recover from a situation where we have lost track of the graphics state.
4559
4560 @c
4561 void mp_gs_unknown_graphics_state (MP mp,scaled c) ;
4562
4563
4564 @d mp_void (null+1) /* a null pointer different from |null| */
4565
4566 @c void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4567   struct _gs_state *p; /* to shift graphic states around */
4568   if ( (c==0)||(c==-1) ) {
4569     if ( mp->ps->gs_state==NULL ) {
4570       mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4571       gs_previous=NULL;
4572     } else {
4573       while ( gs_previous!=NULL ) {
4574         p = gs_previous;
4575         mp_xfree(mp->ps->gs_state);
4576         mp->ps->gs_state=p;
4577       }
4578     }
4579     gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4580     gs_colormodel=mp_uninitialized_model;
4581     gs_ljoin=3;
4582     gs_lcap=3;
4583     gs_miterlim=0;
4584     gs_dash_p=NULL;
4585     gs_dash_sc=0;
4586     gs_width=-1;
4587   } else if ( c==1 ) {
4588     p= mp->ps->gs_state;
4589     mp->ps->gs_state =  mp_xmalloc(mp,1,sizeof(struct _gs_state));
4590     memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4591     gs_previous = p;
4592   } else if ( c==2 ) {
4593     p = gs_previous;
4594     mp_xfree(mp->ps->gs_state);
4595     mp->ps->gs_state=p;
4596   }
4597 }
4598
4599
4600 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4601 that \ps's idea of the graphics state agrees with what is stored in the object.
4602
4603 @<Declarations@>=
4604 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) ;
4605
4606 @ @c 
4607 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) {
4608   /* get ready to output graphical object |p| */
4609   struct mp_knot *pp; /* for list manipulation */
4610   struct mp_dash_list *hh;
4611   scaled wx,wy,ww; /* dimensions of pen bounding box */
4612   boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4613   integer tx,ty; /* temporaries for computing |adj_wx| */
4614   scaled scf; /* a scale factor for the dash pattern */
4615   if ( gr_has_color(p) )
4616     @<Make sure \ps\ will use the right color for object~|p|@>;
4617   if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) )
4618     if ( gr_pen_p(p)!=null )
4619       if ( pen_is_elliptical(gr_pen_p(p)) ) {
4620         @<Generate \ps\ code that sets the stroke width to the
4621           appropriate rounded value@>;
4622         @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4623         @<Decide whether the line cap parameter matters and set it if necessary@>;
4624         @<Set the other numeric parameters as needed for object~|p|@>;
4625       }
4626   if ( mp->ps_offset>0 ) mp_print_ln(mp);
4627 }
4628
4629 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4630 if ( gr_type(p)==mp_stroked_code )
4631   if ( (gr_left_type(gr_path_p(p))==mp_endpoint)||(gr_dash_p(p)!=NULL) )
4632     if ( gs_lcap!=gr_lcap_val(p) ) {
4633       ps_room(13);
4634       mp_print_char(mp, ' ');
4635       mp_print_char(mp, '0'+gr_lcap_val(p)); 
4636       mp_ps_print_cmd(mp, " setlinecap"," lc");
4637       gs_lcap=gr_lcap_val(p);
4638     }
4639
4640 @ @<Set the other numeric parameters as needed for object~|p|@>=
4641 if ( gs_ljoin!=gr_ljoin_val(p) ) {
4642   ps_room(14);
4643   mp_print_char(mp, ' ');
4644   mp_print_char(mp, '0'+gr_ljoin_val(p)); 
4645   mp_ps_print_cmd(mp, " setlinejoin"," lj");
4646   gs_ljoin=gr_ljoin_val(p);
4647 }
4648 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4649   ps_room(27);
4650   mp_print_char(mp, ' ');
4651   mp_print_scaled(mp, gr_miterlim_val(p)); 
4652   mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4653   gs_miterlim=gr_miterlim_val(p);
4654 }
4655
4656 @ @<Make sure \ps\ will use the right color for object~|p|@>=
4657 {
4658   if ( (gr_color_model(p)==mp_rgb_model)||
4659      ((gr_color_model(p)==mp_uninitialized_model)&&
4660      ((mp->internal[mp_default_color_model]>>16)==mp_rgb_model)) ) {
4661   if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=gr_red_val(p))||
4662       (gs_green!=gr_green_val(p))||(gs_blue!=gr_blue_val(p)) ) {
4663       gs_red=gr_red_val(p);
4664       gs_green=gr_green_val(p);
4665       gs_blue=gr_blue_val(p);
4666       gs_black= -1;
4667       gs_colormodel=mp_rgb_model;
4668       { ps_room(36);
4669         mp_print_char(mp, ' ');
4670         mp_print_scaled(mp, gs_red); mp_print_char(mp, ' ');
4671         mp_print_scaled(mp, gs_green); mp_print_char(mp, ' ');
4672         mp_print_scaled(mp, gs_blue);
4673         mp_ps_print_cmd(mp, " setrgbcolor", " R");
4674       }
4675     }
4676   } else if ( (gr_color_model(p)==mp_cmyk_model)||
4677      ((gr_color_model(p)==mp_uninitialized_model)&&
4678      ((mp->internal[mp_default_color_model]>>16)==mp_cmyk_model)) ) {
4679    if ( (gs_red!=gr_cyan_val(p))||(gs_green!=gr_magenta_val(p))||
4680       (gs_blue!=gr_yellow_val(p))||(gs_black!=gr_black_val(p))||
4681       (gs_colormodel!=mp_cmyk_model) ) {
4682       if ( gr_color_model(p)==mp_uninitialized_model ) {
4683         gs_red=0;
4684         gs_green=0;
4685         gs_blue=0;
4686         gs_black=unity;
4687       } else {
4688         gs_red=gr_cyan_val(p);
4689         gs_green=gr_magenta_val(p);
4690         gs_blue=gr_yellow_val(p);
4691         gs_black=gr_black_val(p);
4692       }
4693       gs_colormodel=mp_cmyk_model;
4694       { ps_room(45);
4695         mp_print_char(mp, ' ');
4696         mp_print_scaled(mp, gs_red); mp_print_char(mp, ' ');
4697         mp_print_scaled(mp, gs_green); mp_print_char(mp, ' ');
4698         mp_print_scaled(mp, gs_blue); mp_print_char(mp, ' ');
4699         mp_print_scaled(mp, gs_black);
4700         mp_ps_print_cmd(mp, " setcmykcolor"," C");
4701       }
4702     }
4703   } else if ( (gr_color_model(p)==mp_grey_model)||
4704     ((gr_color_model(p)==mp_uninitialized_model)&&
4705      ((mp->internal[mp_default_color_model]>>16)==mp_grey_model)) ) {
4706    if ( (gs_red!=gr_grey_val(p))||(gs_colormodel!=mp_grey_model) ) {
4707       gs_red = gr_grey_val(p);
4708       gs_green= -1;
4709       gs_blue= -1;
4710       gs_black= -1;
4711       gs_colormodel=mp_grey_model;
4712       { ps_room(16);
4713         mp_print_char(mp, ' ');
4714         mp_print_scaled(mp, gs_red);
4715         mp_ps_print_cmd(mp, " setgray"," G");
4716       }
4717     }
4718   }
4719   if ( gr_color_model(p)==mp_no_model )
4720     gs_colormodel=mp_no_model;
4721 }
4722
4723 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4724 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4725 @:setwidth}{\&{setwidth}command@>
4726 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4727 generate \ps\ code that computes the rounded value.  For non-circular pens, the
4728 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4729 have the computed width.
4730
4731 Rounding the width to whole pixels is not likely to improve the appearance of
4732 diagonal or curved strokes, but we do it anyway for consistency.  The
4733 \&{truncate} command generated here tends to make all the strokes a little
4734 @:truncate}{\&{truncate} command@>
4735 thinner, but this is appropriate for \ps's scan-conversion rules.  Even with
4736 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4737 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4738 to compute in \ps.
4739
4740 @<Generate \ps\ code that sets the stroke width...@>=
4741 @<Set |wx| and |wy| to the width and height of the bounding box for
4742   |pen_p(p)|@>;
4743 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4744   important and set |adj_wx| and |ww| accordingly@>;
4745 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4746   if ( adj_wx ) {
4747     ps_room(13);
4748     mp_print_char(mp, ' '); mp_print_scaled(mp, ww);
4749     mp_ps_print_cmd(mp, 
4750       " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4751   } else {
4752     if ( mp->internal[mp_procset]>0 ) {
4753       ps_room(13);
4754       mp_print_char(mp, ' ');
4755       mp_print_scaled(mp, ww);
4756       mp_ps_print(mp, " vlw");
4757     } else { 
4758       ps_room(15);
4759       mp_print(mp, " 0 "); mp_print_scaled(mp, ww);
4760       mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4761     }
4762   }
4763   gs_width = ww;
4764   gs_adj_wx = adj_wx;
4765 }
4766
4767 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4768 pp=gr_pen_p(p);
4769 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4770   wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4771   wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4772 } else {
4773   wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4774   wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4775 }
4776
4777 @ The path is considered ``essentially horizontal'' if its range of
4778 $y$~coordinates is less than the $y$~range |wy| for the pen.  ``Essentially
4779 vertical'' paths are detected similarly.  This code ensures that no component
4780 of the pen transformation is more that |aspect_bound*(ww+1)|.
4781
4782 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4783     more than this factor */
4784
4785 @d do_x_loc 1
4786 @d do_y_loc 2
4787
4788 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4789 tx=1; ty=1;
4790 if ( mp_gr_coord_rangeOK(gr_path_p(p), do_y_loc, wy) ) tx=aspect_bound;
4791 else if ( mp_gr_coord_rangeOK(gr_path_p(p), do_x_loc, wx) ) ty=aspect_bound;
4792 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
4793 else { ww=wx; adj_wx=true;  }
4794
4795 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4796 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4797 allowable range for $x$ or~$y$.  We do not need and cannot afford a full
4798 bounding-box computation.
4799
4800 @<Declarations@>=
4801 boolean mp_gr_coord_rangeOK (struct mp_knot *h, 
4802                           small_number  zoff, scaled dz);
4803
4804 @ @c
4805 boolean mp_gr_coord_rangeOK (struct mp_knot *h, 
4806                           small_number  zoff, scaled dz) {
4807   struct mp_knot *p; /* for scanning the path form |h| */
4808   scaled zlo,zhi; /* coordinate range so far */
4809   scaled z; /* coordinate currently being tested */
4810   if (zoff==do_x_loc) {
4811     zlo=gr_x_coord(h);
4812     zhi=zlo;
4813     p=h;
4814     while ( gr_right_type(p)!=mp_endpoint ) {
4815       z=gr_right_x(p);
4816       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4817       p=gr_next_knot(p);  z=gr_left_x(p);
4818       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4819       z=gr_x_coord(p);
4820       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4821       if ( p==h ) break;
4822     }
4823   } else {
4824     zlo=gr_y_coord(h);
4825     zhi=zlo;
4826     p=h;
4827     while ( gr_right_type(p)!=mp_endpoint ) {
4828       z=gr_right_y(p);
4829       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4830       p=gr_next_knot(p); z=gr_left_y(p);
4831       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4832       z=gr_y_coord(p);
4833       @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4834       if ( p==h ) break;
4835     }
4836   }
4837   return true;
4838 }
4839
4840 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4841 if ( z<zlo ) zlo=z;
4842 else if ( z>zhi ) zhi=z;
4843 if ( zhi-zlo>dz ) return false
4844
4845 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4846 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4847 @:stroke}{\&{stroke} command@>
4848 @:fill}{\&{fill} command@>
4849 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4850 a reference.
4851
4852 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4853 if ( gr_type(p)==mp_fill_code ) {
4854   hh=NULL;
4855 } else { 
4856   hh=gr_dash_p(p);
4857   scf=mp_gr_get_pen_scale(mp, gr_pen_p(p));
4858   if ( scf==0 ) {
4859     if ( gs_width==0 ) scf=gr_dash_scale(p);  else hh=NULL;
4860   } else { 
4861     scf=mp_make_scaled(mp, gs_width,scf);
4862     scf=mp_take_scaled(mp, scf,gr_dash_scale(p));
4863   }
4864 }
4865 if ( hh==NULL ) {
4866   if ( gs_dash_p!=NULL ) {
4867     mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4868     gs_dash_p=NULL;
4869   }
4870 } else if ( (gs_dash_sc!=scf) || ! mp_gr_same_dashes(gs_dash_p,hh) ) {
4871   @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
4872 }
4873
4874 @ @<Declarations@>=
4875 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) ;
4876
4877
4878 @ @c
4879 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) { 
4880   return mp_sqrt_det(mp, 
4881     gr_left_x(p)-gr_x_coord(p), gr_right_x(p)-gr_x_coord(p),
4882     gr_left_y(p)-gr_y_coord(p), gr_right_y(p)-gr_y_coord(p));
4883 }
4884
4885
4886 @ Translating a dash list into \ps\ is very similar to printing it symbolically
4887 in |print_edges|.  A dash pattern with |dash_y(hh)=0| has length zero and is
4888 ignored.  The same fate applies in the bizarre case of a dash pattern that
4889 cannot be printed without overflow.
4890
4891 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
4892 { gs_dash_p=hh;
4893   gs_dash_sc=scf;
4894   if ( (gr_dash_y(hh)==0) || 
4895        ((abs(gr_dash_y(hh)) / unity) >= (el_gordo / scf))) {
4896     mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4897   } else { 
4898     struct mp_dash_item *dpp=gr_dash_list(hh);
4899     ps_room(28);
4900     mp_print(mp, " [");
4901     while ( dpp!=NULL ) {
4902       mp_ps_pair_out(mp, mp_take_scaled(mp, gr_stop_x(dpp)-gr_start_x(dpp),scf),
4903          mp_take_scaled(mp, gr_start_x(gr_dash_link(dpp))-gr_stop_x(dpp),scf));
4904       dpp=gr_dash_link(dpp);
4905     }
4906     ps_room(22);
4907     mp_print(mp, "] ");
4908     mp_print_scaled(mp, mp_take_scaled(mp, mp_gr_dash_offset(mp, hh),scf));
4909     mp_ps_print_cmd(mp, " setdash"," sd");
4910   }
4911 }
4912
4913 @ @<Declarations@>=
4914 boolean mp_gr_same_dashes (struct mp_dash_list *h, struct mp_dash_list *hh) ;
4915
4916 @ @c
4917 boolean mp_gr_same_dashes (struct mp_dash_list * h, struct mp_dash_list *hh) {
4918   /* do |h| and |hh| represent the same dash pattern? */
4919   struct mp_dash_item * p, *pp; /* dash nodes being compared */
4920   if ( h==hh ) return true;
4921   else if ( (h==NULL)||(hh==NULL) ) return false;
4922   else if ( gr_dash_y(h)!=gr_dash_y(hh) ) return false;
4923   else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
4924   return false; /* can't happen */
4925 }
4926
4927 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
4928 { p=gr_dash_list(h);
4929   pp=gr_dash_list(hh);
4930   while ( (p!=NULL)&&(pp!=NULL) ) {
4931     if ( (gr_start_x(p)!=gr_start_x(pp))||
4932          (gr_stop_x(p)!=gr_stop_x(pp)) ) {
4933       break;
4934     } else { 
4935       p=gr_dash_link(p);
4936       pp=gr_dash_link(pp);
4937     }
4938   }
4939   return (p==pp);
4940 }
4941
4942 @ @<Declarations@>=
4943 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) ;
4944
4945 @ @c 
4946 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) {
4947   scaled x;  /* the answer */
4948   if ( (gr_dash_list(h)==NULL) || (gr_dash_y(h)<0) ) 
4949      mp_confusion(mp, "dash0");
4950 @:this can't happen dash0}{\quad dash0@>
4951   if ( gr_dash_y(h)==0 ) {
4952     x=0; 
4953   } else { 
4954     x=-(gr_start_x(gr_dash_list(h)) % gr_dash_y(h));
4955     if ( x<0 ) x=x+gr_dash_y(h);
4956   }
4957   return x;
4958 }
4959
4960 @ When stroking a path with an elliptical pen, it is necessary to transform
4961 the coordinate system so that a unit circular pen will have the desired shape.
4962 To keep this transformation local, we enclose it in a
4963 $$\&{gsave}\ldots\&{grestore}$$
4964 block. Any translation component must be applied to the path being stroked
4965 while the rest of the transformation must apply only to the pen.
4966 If |fill_also=true|, the path is to be filled as well as stroked so we must
4967 insert commands to do this after giving the path.
4968
4969 @<Declarations@>=
4970 void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) ;
4971
4972
4973 @c void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) {
4974   /* generate an elliptical pen stroke from object |h| */
4975   scaled txx,txy,tyx,tyy; /* transformation parameters */
4976   struct mp_knot *p; /* the pen to stroke with */
4977   scaled d1,det; /* for tweaking transformation parameters */
4978   integer s; /* also for tweaking transformation paramters */
4979   boolean transformed; /* keeps track of whether gsave/grestore are needed */
4980   transformed=false;
4981   @<Use |pen_p(h)| to set the transformation parameters and give the initial
4982     translation@>;
4983   @<Tweak the transformation parameters so the transformation is nonsingular@>;
4984   mp_gr_ps_path_out(mp, gr_path_p(h));
4985   if ( mp->internal[mp_procset]==0 ) {
4986     if ( fill_also ) mp_print_nl(mp, "gsave fill grestore");
4987     @<Issue \ps\ commands to transform the coordinate system@>;
4988     mp_ps_print(mp, " stroke");
4989     if ( transformed ) mp_ps_print(mp, " grestore");
4990   } else {
4991     if ( fill_also ) mp_print_nl(mp, "B"); else mp_print_ln(mp);
4992     if ( (txy!=0)||(tyx!=0) ) {
4993       mp_print(mp, " [");
4994       mp_ps_pair_out(mp, txx,tyx);
4995       mp_ps_pair_out(mp, txy,tyy);
4996       mp_ps_print(mp, "0 0] t");
4997     } else if ((txx!=unity)||(tyy!=unity) )  {
4998       mp_ps_pair_out(mp,txx,tyy);
4999       mp_print(mp, " s");
5000     };
5001     mp_ps_print(mp, " S");
5002     if ( transformed ) mp_ps_print(mp, " Q");
5003   }
5004   mp_print_ln(mp);
5005 }
5006
5007 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5008 p=gr_pen_p(h);
5009 txx=gr_left_x(p);
5010 tyx=gr_left_y(p);
5011 txy=gr_right_x(p);
5012 tyy=gr_right_y(p);
5013 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5014   mp_print_nl(mp, ""); 
5015   mp_ps_print_cmd(mp, "gsave ","q ");
5016   mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5017   mp_ps_print(mp, "translate ");
5018   txx-=gr_x_coord(p);
5019   tyx-=gr_y_coord(p);
5020   txy-=gr_x_coord(p);
5021   tyy-=gr_y_coord(p);
5022   transformed=true;
5023 } else {
5024   mp_print_nl(mp, "");
5025 }
5026 @<Adjust the transformation to account for |gs_width| and output the
5027   initial \&{gsave} if |transformed| should be |true|@>
5028
5029 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5030 if ( gs_width!=unity ) {
5031   if ( gs_width==0 ) { 
5032     txx=unity; tyy=unity;
5033   } else { 
5034     txx=mp_make_scaled(mp, txx,gs_width);
5035     txy=mp_make_scaled(mp, txy,gs_width);
5036     tyx=mp_make_scaled(mp, tyx,gs_width);
5037     tyy=mp_make_scaled(mp, tyy,gs_width);
5038   };
5039 }
5040 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5041   if ( (! transformed) ){ 
5042     mp_ps_print_cmd(mp, "gsave ","q ");
5043     transformed=true;
5044   }
5045 }
5046
5047 @ @<Issue \ps\ commands to transform the coordinate system@>=
5048 if ( (txy!=0)||(tyx!=0) ){ 
5049   mp_print_ln(mp);
5050   mp_print_char(mp, '[');
5051   mp_ps_pair_out(mp, txx,tyx);
5052   mp_ps_pair_out(mp, txy,tyy);
5053   mp_ps_print(mp, "0 0] concat");
5054 } else if ( (txx!=unity)||(tyy!=unity) ){ 
5055   mp_print_ln(mp);
5056   mp_ps_pair_out(mp, txx,tyy);
5057   mp_print(mp, "scale");
5058 }
5059
5060 @ The \ps\ interpreter will probably abort if it encounters a singular
5061 transformation matrix.  The determinant must be large enough to ensure that
5062 the printed representation will be nonsingular.  Since the printed
5063 representation is always within $2^{-17}$ of the internal |scaled| value, the
5064 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5065 the magnitudes of |txx/65536|, |txy/65536|, etc.
5066
5067 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5068 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5069
5070 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5071 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5072 d1=4*aspect_bound+1;
5073 if ( abs(det)<d1 ) { 
5074   if ( det>=0 ) { d1=d1-det; s=1;  }
5075   else { d1=-d1-det; s=-1;  };
5076   d1=d1*unity;
5077   if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5078     if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5079     else txx=txx+(d1+s*abs(tyy)) / tyy;
5080   } else {
5081     if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5082     else txy=txy+(d1+s*abs(tyx)) / tyx;
5083   }
5084 }
5085
5086 @ Here is a simple routine that just fills a cycle.
5087
5088 @<Declarations@>=
5089 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p);
5090
5091 @ @c
5092 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p) { /* fill cyclic path~|p| */
5093   mp_gr_ps_path_out(mp, p);
5094   mp_ps_print_cmd(mp, " fill"," F");
5095   mp_print_ln(mp);
5096 }
5097
5098 @ A text node may specify an arbitrary transformation but the usual case
5099 involves only shifting, scaling, and occasionally rotation.  The purpose
5100 of |choose_scale| is to select a scale factor so that the remaining
5101 transformation is as ``nice'' as possible.  The definition of ``nice''
5102 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5103 nice because they work out well for bitmap fonts.  The code here selects
5104 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5105 non-shifting part of the transformation matrix.  It is careful to avoid
5106 additions that might cause undetected overflow.
5107
5108 @<Declarations@>=
5109 scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) ;
5110
5111 @ @c scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) {
5112   /* |p| should point to a text node */
5113   scaled a,b,c,d,ad,bc; /* temporary values */
5114   a=gr_txx_val(p);
5115   b=gr_txy_val(p);
5116   c=gr_tyx_val(p);
5117   d=gr_tyy_val(p);
5118   if ( (a<0) ) negate(a);
5119   if ( (b<0) ) negate(b);
5120   if ( (c<0) ) negate(c);
5121   if ( (d<0) ) negate(d);
5122   ad=half(a-d);
5123   bc=half(b-c);
5124   return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5125 }
5126
5127
5128
5129 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5130
5131 @c 
5132 void mp_gr_ship_out (MP mp, struct mp_graphic_object *h) {
5133   struct mp_graphic_object *p;
5134   scaled ds,scf; /* design size and scale factor for a text node */
5135   boolean transformed; /* is the coordinate system being transformed? */
5136   p =  h;
5137   while ( p!=NULL ) { 
5138     if ( gr_has_color(p) ) {
5139       if ( (gr_pre_script(p))!=NULL ) {
5140         mp_print_nl (mp, gr_pre_script(p)); 
5141         mp_print_ln(mp);
5142       }
5143     }
5144     mp_gr_fix_graphics_state(mp, p);
5145     switch (gr_type(p)) {
5146     case mp_fill_code: 
5147       if ( gr_pen_p(p)==NULL ) mp_gr_ps_fill_out(mp, gr_path_p(p));
5148       else if ( pen_is_elliptical(gr_pen_p(p)) ) mp_gr_stroke_ellipse(mp, p,true);
5149       else { 
5150         /* NOTE: these have to be the result of 
5151            |mp_make_envelope(mp, mp_copy_path(path_p(p),p ...))|
5152          add
5153            |mp_make_envelope(mp, mp_htap_ypoc(path_p(p),p ...))|
5154          */
5155         mp_gr_ps_fill_out(mp, gr_path_p(p));
5156         mp_gr_ps_fill_out(mp, gr_htap_p(p));
5157       }
5158       if ( gr_post_script(p)!=NULL ) {
5159          mp_print_nl (mp, gr_post_script(p)); mp_print_ln(mp);
5160       }
5161       break;
5162     case mp_stroked_code:
5163       if ( pen_is_elliptical(gr_pen_p(p)) ) mp_gr_stroke_ellipse(mp, p,false);
5164       else { 
5165         /* NOTE: this has to be the result of :
5166           |q=mp_gr_copy_path(mp, gr_path_p(p));|
5167           |t=gr_lcap_val(p);|
5168           |@<Break the cycle and set |t:=1| if path |q| is cyclic@>;|
5169           |q=mp_make_envelope(mp, q, pen_p(p),ljoin_val(p),t,miterlim_val(p));|
5170          */
5171         mp_gr_ps_fill_out(mp, gr_path_p(p));
5172       }
5173       if ( gr_post_script(p)!=NULL ) {
5174         mp_print_nl (mp, gr_post_script(p)); mp_print_ln(mp);
5175       }
5176       break;
5177     case mp_text_code: 
5178       if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5179         if ( mp->internal[mp_prologues]>0 )
5180           scf=mp_gr_choose_scale(mp, p);
5181         else 
5182           scf=mp_indexed_size(mp, gr_font_n(p), gr_name_type(p));
5183         @<Shift or transform as necessary before outputting text node~|p| at scale
5184           factor~|scf|; set |transformed:=true| if the original transformation must
5185           be restored@>;
5186         mp_ps_string_out(mp, gr_text_p(p));
5187         mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5188         @<Print the size information and \ps\ commands for text node~|p|@>;
5189         mp_print_ln(mp);
5190       }
5191       if ( gr_post_script(p)!=NULL ) {
5192         mp_print_nl (mp, gr_post_script(p)); mp_print_ln(mp);
5193       }
5194       break;
5195     case mp_start_clip_code: 
5196       mp_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5197       mp_gr_ps_path_out(mp, gr_path_p(p));
5198       mp_ps_print_cmd(mp, " clip"," W");
5199       mp_print_ln(mp);
5200       if ( mp->internal[mp_restore_clip_color]>0 )
5201         mp_gs_unknown_graphics_state(mp, 1);
5202       break;
5203     case mp_stop_clip_code: 
5204       mp_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5205       mp_print_ln(mp);
5206       if ( mp->internal[mp_restore_clip_color]>0 )
5207         mp_gs_unknown_graphics_state(mp, 2);
5208       else
5209         mp_gs_unknown_graphics_state(mp, -1);
5210       break;
5211     case mp_start_bounds_code:
5212     case mp_stop_bounds_code:
5213           break;
5214     } /* all cases are enumerated */
5215     p=gr_link(p);
5216   }
5217 }
5218
5219 @ The envelope of a cyclic path~|q| could be computed by calling
5220 |make_envelope| once for |q| and once for its reversal.  We don't do this
5221 because it would fail color regions that are covered by the pen regardless
5222 of where it is placed on~|q|.
5223
5224 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5225 if ( gr_left_type(q)!=mp_endpoint ) { 
5226   gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5227   gr_right_type(q)=mp_endpoint;
5228   q=gr_next_knot(q);
5229   t=1;
5230 }
5231
5232 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5233 ps_room(18);
5234 mp_print_char(mp, ' ');
5235 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5236 mp_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5237 mp_print(mp, " fshow");
5238 if ( transformed ) 
5239    mp_ps_print_cmd(mp, " grestore"," Q")
5240
5241
5242
5243 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5244 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5245             (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5246 if ( transformed ) {
5247   mp_ps_print_cmd(mp, "gsave [", "q [");
5248   mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5249                      mp_make_scaled(mp, gr_tyx_val(p),scf));
5250   mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5251                      mp_make_scaled(mp, gr_tyy_val(p),scf));
5252   mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5253   mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5254 } else { 
5255   mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5256   mp_ps_print_cmd(mp, "moveto","m");
5257 }
5258 mp_print_ln(mp)
5259