cleanups in sync with mp.w
[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 incr(A)   (A)=(A)+1 /* increase a variable by unity */
51 @d decr(A)   (A)=(A)-1 /* decrease a variable by unity */
52 @d negate(A)   (A)=-(A) /* change the sign of a variable */
53 @d odd(A)   ((A)%2==1)
54 @d print_err(A) mp_print_err(mp,(A))
55
56 @c
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <stdarg.h>
61 #include <assert.h>
62 #include "avl.h"
63 #include "mplib.h"
64 #include "mpmp.h" /* internal header */
65 #include "mppsout.h" /* internal header */
66 @h
67 @<Declarations@>;
68 @<Static variables in the outer block@>;
69
70 @ There is a small bit of code from the backend that bleads through
71 to the frontend because I do not know how to set up the includes
72 properly. Those are the definitions of |struct libavl_allocator|
73 and |typedef struct psout_data_struct * psout_data|.
74
75 The |libavl_allocator| is a trick that makes sure that frontends 
76 do not need |avl.h|, and the |psout_data| is needed for the backend 
77 data structure.
78
79 @ @(mppsout.h@>=
80 @<Types...@>;
81 typedef struct psout_data_struct {
82   @<Globals@>;
83 } psout_data_struct ;
84 @<Exported function headers@>
85
86 @ @<Exported function headers@>=
87 void mp_backend_initialize (MP mp) ;
88 void mp_backend_free (MP mp) ;
89
90 @
91 @c void mp_backend_initialize (MP mp) {
92   mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
93   @<Set initial values@>;
94 }
95 void mp_backend_free (MP mp) {
96   @<Dealloc variables@>;
97   enc_free(mp);
98   t1_free(mp);
99   fm_free(mp);
100   mp_xfree(mp->ps);
101   mp->ps = NULL;
102 }
103
104
105 @* Traditional {psfonts.map} loading.
106
107 TODO: It is likely that this code can be removed after a few minor tweaks.
108
109 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
110 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
111 can be used when |internal[prologues]>0|.  Each line consists of a \TeX\ name,
112 one or more spaces, a PostScript name, and possibly a space and some other junk.
113 This routine reads the table, updates |font_ps_name| entries starting after
114 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.  If the file |ps_tab_file|
115 is missing, we assume that the existing font names are OK and nothing needs to
116 be done.
117
118 @d ps_tab_name "psfonts.map"  /* locates font name translation table */
119
120 @<Declarations@>=
121 static void mp_read_psname_table (MP mp) ;
122
123 @ @c static void mp_read_psname_table (MP mp) {
124   font_number k; /* font for possible name match */
125   unsigned int lmax; /* upper limit on length of name to match */
126   unsigned int j; /* characters left to read before string gets too long */
127   char *s; /* possible font name to match */
128   text_char c=0; /* character being read from |ps_tab_file| */
129   if ( (mp->ps->ps_tab_file = mp_open_file(mp, ps_tab_name, "r", mp_filetype_fontmap)) ) {
130     @<Set |lmax| to the maximum |font_name| length for fonts
131       |last_ps_fnum+1| through |last_fnum|@>;
132     while (! feof(mp->ps->ps_tab_file) ) {
133       @<Read at most |lmax| characters from |ps_tab_file| into string |s|
134         but |goto common_ending| if there is trouble@>;
135       for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
136         if ( mp_xstrcmp(s,mp->font_name[k])==0 ) {
137           @<|flush_string(s)|, read in |font_ps_name[k]|, and
138             |goto common_ending|@>;
139         }
140       }
141       mp_xfree(s);
142     COMMON_ENDING:
143       c = fgetc(mp->ps->ps_tab_file);
144           if (c=='\r') {
145         c = fgetc(mp->ps->ps_tab_file);
146         if (c!='\n') 
147           ungetc(c,mp->ps->ps_tab_file);
148       }
149     }
150     mp->last_ps_fnum=mp->last_fnum;
151     fclose(mp->ps->ps_tab_file);
152   }
153 }
154
155 @ @<Glob...@>=
156 FILE * ps_tab_file; /* file for font name translation table */
157
158 @ @<Set |lmax| to the maximum |font_name| length for fonts...@>=
159 lmax=0;
160 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
161   if (strlen(mp->font_name[k])>lmax ) 
162     lmax=strlen(mp->font_name[k]);
163 }
164
165 @ If we encounter the end of line before we have started reading
166 characters from |ps_tab_file|, we have found an entirely blank 
167 line and we skip over it.  Otherwise, we abort if the line ends 
168 prematurely.  If we encounter a comment character, we also skip 
169 over the line, since recent versions of \.{dvips} allow comments
170 in the font map file.
171
172 TODO: this is probably not safe in the case of a really 
173 broken font map file.
174
175 @<Read at most |lmax| characters from |ps_tab_file| into string |s|...@>=
176 s=mp_xmalloc(mp,lmax+1,1);
177 j=0;
178 while (1) { 
179   if (c == '\n' || c == '\r' ) {
180     if (j==0) {
181       mp_xfree(s); s=NULL; goto COMMON_ENDING;
182     } else {
183       mp_fatal_error(mp, "The psfont map file is bad!");
184     }
185   }
186   c = fgetc(mp->ps->ps_tab_file);
187   if (c=='%' || c=='*' || c==';' || c=='#' ) {
188     mp_xfree(s); s=NULL; goto COMMON_ENDING;
189   }
190   if (c==' ' || c=='\t') break;
191   if (j<lmax) {
192    s[j++] = mp->xord[c];
193   } else { 
194     mp_xfree(s); s=NULL; goto COMMON_ENDING;
195   }
196 }
197 s[j]=0
198
199 @ PostScript font names should be at most 28 characters long but we allow 32
200 just to be safe.
201
202 @<|flush_string(s)|, read in |font_ps_name[k]|, and...@>=
203
204   char *ps_name =NULL;
205   mp_xfree(s);
206   do {  
207     if (c=='\n' || c == '\r') 
208       mp_fatal_error(mp, "The psfont map file is bad!");
209     c = fgetc(mp->ps->ps_tab_file);
210   } while (c==' ' || c=='\t');
211   ps_name = mp_xmalloc(mp,33,1);
212   j=0;
213   do {  
214     if (j>31) {
215       mp_fatal_error(mp, "The psfont map file is bad!");
216     }
217     ps_name[j++] = mp->xord[c];
218     if (c=='\n' || c == '\r')
219       c=' ';  
220     else 
221       c = fgetc(mp->ps->ps_tab_file);
222   } while (c != ' ' && c != '\t');
223   ps_name[j]= 0;
224   mp_xfree(mp->font_ps_name[k]);
225   mp->font_ps_name[k]=ps_name;
226   goto COMMON_ENDING;
227 }
228
229
230
231 @* \[44a] Dealing with font encodings.
232
233 First, here are a few helpers for parsing files
234
235 @d check_buf(size, buf_size)
236     if ((unsigned)(size) > (unsigned)(buf_size)) {
237       char s[128];
238       snprintf(s,128,"buffer overflow: (%d,%d) at file %s, line %d",
239                size,buf_size, __FILE__,  __LINE__ );
240       mp_fatal_error(mp,s);
241     }
242
243 @d append_char_to_buf(c, p, buf, buf_size) do {
244     if (c == 9)
245         c = 32;
246     if (c == 13 || c == EOF)
247         c = 10;
248     if (c != ' ' || (p > buf && p[-1] != 32)) {
249         check_buf(p - buf + 1, (buf_size));
250         *p++ = c; 
251     }
252 } while (0)
253
254 @d append_eol(p, buf, buf_size) do {
255     check_buf(p - buf + 2, (buf_size));
256     if (p - buf > 1 && p[-1] != 10)
257         *p++ = 10;
258     if (p - buf > 2 && p[-2] == 32) {
259         p[-2] = 10;
260         p--;
261     }
262     *p = 0;
263 } while (0)
264
265 @d remove_eol(p, buf) do {
266     p = strend(buf) - 1;
267     if (*p == 10)
268         *p = 0;
269 } while (0)
270
271 @d skip(p, c)   if (*p == c)  p++
272 @d strend(s)    strchr(s, 0)
273 @d str_prefix(s1, s2)  (strncmp((s1), (s2), strlen(s2)) == 0)
274
275
276 @ @<Types...@>=
277 typedef struct {
278     boolean loaded;             /* the encoding has been loaded? */
279     char *file_name;                 /* encoding file name */
280     char *enc_name;              /* encoding true name */
281     integer objnum;             /* object number */
282     char **glyph_names;
283     integer tounicode;          /* object number of associated ToUnicode entry */
284 } enc_entry;
285
286
287
288
289 @d ENC_STANDARD  0
290 @d ENC_BUILTIN   1
291
292 @<Glob...@>=
293 #define ENC_BUF_SIZE  0x1000
294 char enc_line[ENC_BUF_SIZE];
295 FILE *enc_file;
296
297
298 @d enc_getchar()   getc(mp->ps->enc_file)
299 @d enc_eof()       feof(mp->ps->enc_file)
300 @d enc_close()     fclose(mp->ps->enc_file)
301
302 @c 
303 static boolean mp_enc_open (MP mp, char *n) {
304   mp->ps->enc_file=mp_open_file(mp, n, "rb", mp_filetype_encoding);
305   if (mp->ps->enc_file!=NULL)
306     return true;
307   else
308    return false;
309 }
310 static void mp_enc_getline (MP mp) {
311   char *p;
312   int c;
313 RESTART:
314   if (enc_eof ()) {
315     print_err("unexpected end of file");
316     mp_error(mp);
317   }
318   p = mp->ps->enc_line;
319   do {
320     c = enc_getchar ();
321     append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
322   } while (c != 10);
323   append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
324   if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
325     goto RESTART;
326 }
327 static void mp_load_enc (MP mp, char *enc_name, 
328                   char **enc_encname, char **glyph_names){
329   char buf[ENC_BUF_SIZE], *p, *r;
330   int names_count;
331   char *myname;
332   int save_selector = mp->selector;
333   if (!mp_enc_open (mp,enc_name)) {
334       mp_print (mp,"cannot open encoding file for reading");
335       return;
336   }
337   mp_normalize_selector(mp);
338   mp_print (mp,"{");
339   mp_print (mp, enc_name);
340   mp_enc_getline (mp);
341   if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
342     remove_eol (r, mp->ps->enc_line);
343     print_err ("invalid encoding vector (a name or `[' missing): `");
344     mp_print(mp,mp->ps->enc_line);
345     mp_print(mp,"'");
346     mp_error(mp);
347   }
348   while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
349   myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
350   memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
351   *(myname+(r-mp->ps->enc_line-1))=0;
352   *enc_encname = myname;
353   while (*r!='[') r++;
354   r++;                        /* skip '[' */
355   names_count = 0;
356   skip (r, ' ');
357   for (;;) {
358     while (*r == '/') {
359       for (p = buf, r++;
360            *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
361         *p = 0;
362       skip (r, ' ');
363       if (names_count > 256) {
364         print_err ("encoding vector contains more than 256 names");
365         mp_error(mp);
366       }
367       if (mp_xstrcmp (buf, notdef) != 0)
368         glyph_names[names_count] = mp_xstrdup (mp,buf);
369       names_count++;
370     }
371     if (*r != 10 && *r != '%') {
372       if (str_prefix (r, "] def"))
373         goto DONE;
374       else {
375         remove_eol (r, mp->ps->enc_line);
376         print_err
377           ("invalid encoding vector: a name or `] def' expected: `");
378         mp_print(mp,mp->ps->enc_line);
379         mp_print(mp,"'");
380         mp_error(mp);
381       }
382     }
383     mp_enc_getline (mp);
384     r = mp->ps->enc_line;
385   }
386 DONE:
387   enc_close ();
388   mp_print (mp,"}");
389   mp->selector = save_selector;
390 }
391 static void mp_read_enc (MP mp, enc_entry * e) {
392     if (e->loaded)
393         return;
394     e->enc_name = NULL;
395     mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
396     e->loaded = true;
397 }
398
399 @ |write_enc| is used to write either external encoding (given in map file) or
400  internal encoding (read from the font file); when |glyph_names| is NULL
401  the 2nd argument is a pointer to the encoding entry; otherwise the 3rd is 
402  the object number of the Encoding object
403  
404 @c
405 static void mp_write_enc (MP mp, char **glyph_names, enc_entry * e) {
406     int i;
407     int s;
408     int foffset;
409     char **g;
410     if (glyph_names == NULL) {
411         if (e->objnum != 0)     /* the encoding has been written already */
412             return;
413         e->objnum = 1;
414         g = e->glyph_names;
415     } else {
416         g = glyph_names;
417     }
418
419     mp_print(mp,"\n%%%%BeginResource: encoding ");
420     mp_print(mp, e->enc_name);
421     mp_print(mp, "\n/");
422     mp_print(mp, e->enc_name);
423     mp_print(mp, " [ ");
424     foffset = strlen(e->file_name)+3;
425     for (i = 0; i < 256; i++) {
426       s = strlen(g[i]);
427       if (s+1+foffset>=80) {
428             mp_print_ln (mp);
429         foffset = 0;
430       }
431       foffset += s+2;
432       mp_print_char(mp,'/');
433       mp_print(mp, g[i]);
434       mp_print_char(mp,' ');
435     }
436     if (foffset>75)
437            mp_print_ln (mp);
438     mp_print_nl (mp,"] def\n");
439     mp_print(mp,"%%%%EndResource");
440 }
441
442
443 @ All encoding entries go into AVL tree for fast search by name.
444
445 @<Glob...@>=
446 struct avl_table *enc_tree;
447
448 @ Memory management functions for avl 
449
450 @<Static variables in the outer block@>=
451 static const char notdef[] = ".notdef";
452
453 @ @<Declarations@>=
454 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
455 static void avl_xfree (struct libavl_allocator *allocator, void *block);
456
457 @ @c
458 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
459     assert(allocator);
460     return malloc (size);
461 }
462 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
463     assert(allocator);
464     free (block);
465 }
466
467 @ @<Glob...@>=
468 struct libavl_allocator avl_xallocator;
469
470 @ @<Set initial...@>=
471 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
472 mp->ps->avl_xallocator.libavl_free= avl_xfree;
473 mp->ps->enc_tree = NULL;
474
475 @ @c
476 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
477     assert(p==NULL);
478     return strcmp (((const enc_entry *) pa)->file_name,
479                    ((const enc_entry *) pb)->file_name);
480 }
481 static enc_entry * mp_add_enc (MP mp, char *s) {
482     int i;
483     enc_entry tmp, *p;
484     void **aa;
485     if (mp->ps->enc_tree == NULL) {
486       mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
487     }
488     tmp.file_name = s;
489     p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
490     if (p != NULL)              /* encoding already registered */
491         return p;
492     p = mp_xmalloc (mp,1,sizeof (enc_entry));
493     p->loaded = false;
494     p->file_name = mp_xstrdup (mp,s);
495     p->objnum = 0;
496     p->tounicode = 0;
497     p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
498     for (i = 0; i < 256; i++)
499         p->glyph_names[i] = (char *) notdef;
500     aa = avl_probe (mp->ps->enc_tree, p);
501     return p;
502 }
503
504 @ cleaning up... 
505
506 @c 
507 static void mp_destroy_enc_entry (void *pa, void *pb) {
508     enc_entry *p;
509     int i;
510
511     p = (enc_entry *) pa;
512     assert(pb==NULL);
513     mp_xfree (p->file_name);
514     if (p->glyph_names != NULL)
515         for (i = 0; i < 256; i++)
516             if (p->glyph_names[i] != notdef)
517                 mp_xfree (p->glyph_names[i]);
518     mp_xfree (p->glyph_names);
519     mp_xfree (p);
520 }
521
522 @ @<Declarations@>=
523 static void enc_free (MP mp);
524
525 @ @c static void enc_free (MP mp) {
526     if (mp->ps->enc_tree != NULL)
527       avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
528 }
529
530 @ @<Exported function headers@>=
531 void mp_reload_encodings (MP mp) ;
532
533 @ @<Declarations@>=
534 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) ;
535
536 @ @c void mp_reload_encodings (MP mp) {
537   int f;
538   enc_entry *e;
539   fm_entry *fm_cur;
540   int lastfnum = mp->last_fnum;
541   for (f=null_font+1;f<=lastfnum;f++) {
542     if (mp->font_enc_name[f]!=NULL ) {
543        mp_xfree(mp->font_enc_name[f]);
544        mp->font_enc_name[f]=NULL;
545     }
546     if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f,&fm_cur)) { 
547       if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
548         e = fm_cur->encoding;
549         mp_read_enc (mp,e);
550       }
551     }
552   }
553 }
554 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) {
555   int f;
556   enc_entry *e;
557   fm_entry *fm;
558   for (f=null_font+1;f<=lastfnum;f++) {
559     if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) { 
560       if (fm != NULL && (fm->ps_name != NULL)) {
561         if (is_reencoded (fm)) {
562           if (encodings_only || (!is_subsetted (fm))) {
563             e = fm->encoding;
564             mp_write_enc (mp,NULL, e);
565             /* clear for next run */
566             e->objnum = 0;
567           }
568         }
569       }
570     }
571   }
572 }
573
574 @* \[44b] Parsing font map files.
575
576 @d FM_BUF_SIZE     1024
577
578 @<Glob...@>=
579 FILE *fm_file;
580
581 @
582 @d fm_close()      fclose(mp->ps->fm_file)
583 @d fm_getchar()    fgetc(mp->ps->fm_file)
584 @d fm_eof()        feof(mp->ps->fm_file)
585
586 @<Types...@>=
587 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
588 enum _ltype { MAPFILE, MAPLINE };
589 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
590 typedef struct mitem {
591     int mode;                   /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
592     int type;                   /* map file or map line */
593     char *map_line;              /* pointer to map file name or map line */
594     int lineno;                 /* line number in map file */
595 } mapitem;
596
597 @ @<Glob...@>=
598 mapitem *mitem;
599 fm_entry *fm_cur;
600 fm_entry *loaded_tfm_found;
601 fm_entry *avail_tfm_found;
602 fm_entry *non_tfm_found;
603 fm_entry *not_avail_tfm_found;
604
605 @ @<Set initial...@>=
606 mp->ps->mitem = NULL;
607
608 @ @<Declarations@>=
609 static const char nontfm[] = "<nontfm>";
610
611 @
612 @d read_field(r, q, buf) do {
613     q = buf;
614     while (*r != ' ' && *r != '\0')
615         *q++ = *r++;
616     *q = '\0';
617     skip (r, ' ');
618 } while (0)
619
620 @d set_field(F) do {
621     if (q > buf)
622         fm->F = mp_xstrdup(mp,buf);
623     if (*r == '\0')
624         goto DONE;
625 } while (0)
626
627 @d cmp_return(a, b)
628     if (a > b)
629         return 1;
630     if (a < b)
631         return -1
632
633 @c
634 static fm_entry *new_fm_entry (MP mp) {
635     fm_entry *fm;
636     fm = mp_xmalloc (mp,1,sizeof(fm_entry));
637     fm->tfm_name = NULL;
638     fm->ps_name = NULL;
639     fm->flags = 4;
640     fm->ff_name = NULL;
641     fm->subset_tag = NULL;
642     fm->encoding = NULL;
643     fm->tfm_num = null_font;
644     fm->tfm_avail = TFM_UNCHECKED;
645     fm->type = 0;
646     fm->slant = 0;
647     fm->extend = 0;
648     fm->ff_objnum = 0;
649     fm->fn_objnum = 0;
650     fm->fd_objnum = 0;
651     fm->charset = NULL;
652     fm->all_glyphs = false;
653     fm->links = 0;
654     fm->pid = -1;
655     fm->eid = -1;
656     return fm;
657 }
658
659 static void delete_fm_entry (fm_entry * fm) {
660     mp_xfree (fm->tfm_name);
661     mp_xfree (fm->ps_name);
662     mp_xfree (fm->ff_name);
663     mp_xfree (fm->subset_tag);
664     mp_xfree (fm->charset);
665     mp_xfree (fm);
666 }
667
668 static ff_entry *new_ff_entry (MP mp) {
669     ff_entry *ff;
670     ff = mp_xmalloc (mp,1,sizeof(ff_entry));
671     ff->ff_name = NULL;
672     ff->ff_path = NULL;
673     return ff;
674 }
675
676 static void delete_ff_entry (ff_entry * ff) {
677     mp_xfree (ff->ff_name);
678     mp_xfree (ff->ff_path);
679     mp_xfree (ff);
680 }
681
682 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
683     static char buf[SMALL_BUF_SIZE];
684     char *p = tfmname, *r = strend (p) - 1, *q = r;
685     while (q > p && isdigit (*q))
686         --q;
687     if (!(q > p) || q == r || (*q != '+' && *q != '-'))
688         return NULL;
689     check_buf (q - p + 1, SMALL_BUF_SIZE);
690     strncpy (buf, p, (size_t) (q - p));
691     buf[q - p] = '\0';
692     *i = atoi (q);
693     return buf;
694 }
695
696 @ @<Exported function headers@>=
697 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
698
699 @ @c
700 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
701     fm_entry *res = NULL;
702     res = mp_fm_lookup (mp, f);
703     if (fm != NULL) {
704        *fm =res;
705     }
706     return (res != NULL);
707 }
708
709 @ @<Glob...@>=
710 struct avl_table *tfm_tree;
711 struct avl_table *ps_tree;
712 struct avl_table *ff_tree;
713
714 @ @<Set initial...@>=
715 mp->ps->tfm_tree = NULL;
716 mp->ps->ps_tree = NULL;
717 mp->ps->ff_tree = NULL;
718
719 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
720
721 @c
722 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
723     assert(p==NULL);
724     return strcmp (((const fm_entry *) pa)->tfm_name,
725                    ((const fm_entry *) pb)->tfm_name);
726 }
727
728 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
729
730 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
731     assert(p==NULL);
732     const fm_entry *p1 = (const fm_entry *) pa, *p2 = (const fm_entry *) pb;
733     int i;
734     assert (p1->ps_name != NULL && p2->ps_name != NULL);
735     if ((i = strcmp (p1->ps_name, p2->ps_name)))
736         return i;
737     cmp_return (p1->slant, p2->slant);
738     cmp_return (p1->extend, p2->extend);
739     if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
740         (i = strcmp (p1->tfm_name, p2->tfm_name)))
741         return i;
742     return 0;
743 }
744
745 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
746
747 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
748     assert(p==NULL);
749     return strcmp (((const ff_entry *) pa)->ff_name,
750                    ((const ff_entry *) pb)->ff_name);
751 }
752
753 @ @c static void create_avl_trees (MP mp) {
754     if (mp->ps->tfm_tree == NULL) {
755         mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
756         assert (mp->ps->tfm_tree != NULL);
757     }
758     if (mp->ps->ps_tree == NULL) {
759         mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
760         assert (mp->ps->ps_tree != NULL);
761     }
762     if (mp->ps->ff_tree == NULL) {
763         mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
764         assert (mp->ps->ff_tree != NULL);
765     }
766 }
767
768 @ The function |avl_do_entry| is not completely symmetrical with regards
769 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
770 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
771 with the original version.
772
773 @d LINK_TFM            0x01
774 @d LINK_PS             0x02
775 @d set_tfmlink(fm)     ((fm)->links |= LINK_TFM)
776 @d set_pslink(fm)      ((fm)->links |= LINK_PS)
777 @d unset_tfmlink(fm)   ((fm)->links &= ~LINK_TFM)
778 @d unset_pslink(fm)    ((fm)->links &= ~LINK_PS)
779 @d has_tfmlink(fm)     ((fm)->links & LINK_TFM)
780 @d has_pslink(fm)      ((fm)->links & LINK_PS)
781
782 @c
783 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
784     fm_entry *p;
785     void *a;
786     void **aa;
787     char s[128];
788
789     /* handle |tfm_name| link */
790
791     if (strcmp (fp->tfm_name, nontfm)) {
792         p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
793         if (p != NULL) {
794             if (mode == FM_DUPIGNORE) {
795                snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
796                      fp->tfm_name);
797                 mp_warn(mp,s);
798                 goto exit;
799             } else {            /* mode == |FM_REPLACE| / |FM_DELETE| */
800                 if (mp_has_font_size(mp,p->tfm_num)) {
801                     snprintf(s,128,
802                         "fontmap entry for `%s' has been used, replace/delete not allowed",
803                          fp->tfm_name);
804                     mp_warn(mp,s);
805                     goto exit;
806                 }
807                 a = avl_delete (mp->ps->tfm_tree, p);
808                 assert (a != NULL);
809                 unset_tfmlink (p);
810                 if (!has_pslink (p))
811                     delete_fm_entry (p);
812             }
813         }
814         if (mode != FM_DELETE) {
815             aa = avl_probe (mp->ps->tfm_tree, fp);
816             assert (aa != NULL);
817             set_tfmlink (fp);
818         }
819     }
820
821     /* handle |ps_name| link */
822
823     if (fp->ps_name != NULL) {
824         assert (fp->tfm_name != NULL);
825         p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
826         if (p != NULL) {
827             if (mode == FM_DUPIGNORE) {
828                 snprintf(s,128,
829                     "ps_name entry for `%s' already exists, duplicates ignored",
830                      fp->ps_name);
831                 mp_warn(mp,s);
832                 goto exit;
833             } else {            /* mode == |FM_REPLACE| / |FM_DELETE| */
834                 if (mp_has_font_size(mp,p->tfm_num)) {
835                     /* REPLACE/DELETE not allowed */
836                     snprintf(s,128,
837                         "fontmap entry for `%s' has been used, replace/delete not allowed",
838                          p->tfm_name);
839                     mp_warn(mp,s);
840                     goto exit;
841                 }
842                 a = avl_delete (mp->ps->ps_tree, p);
843                 assert (a != NULL);
844                 unset_pslink (p);
845                 if (!has_tfmlink (p))
846                     delete_fm_entry (p);
847             }
848         }
849         if (mode != FM_DELETE) {
850             aa = avl_probe (mp->ps->ps_tree, fp);
851             assert (aa != NULL);
852             set_pslink (fp);
853         }
854     }
855   exit:
856     if (!has_tfmlink (fp) && !has_pslink (fp))  /* e. g. after |FM_DELETE| */
857         return 1;               /* deallocation of |fm_entry| structure required */
858     else
859         return 0;
860 }
861
862 @ consistency check for map entry, with warn flag 
863
864 @c
865 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
866     int a = 0;
867     char s[128];
868     assert (fm != NULL);
869     if (fm->ps_name != NULL) {
870         if (is_basefont (fm)) {
871             if (is_fontfile (fm) && !is_included (fm)) {
872                 if (warn) {
873                     snprintf(s,128, "invalid entry for `%s': "
874                          "font file must be included or omitted for base fonts",
875                          fm->tfm_name);
876                     mp_warn(mp,s);
877                 }
878                 a += 1;
879             }
880         } else {                /* not a base font */
881             /* if no font file given, drop this entry */
882             /* |if (!is_fontfile (fm)) {
883                  if (warn) {
884                    snprintf(s,128, 
885                         "invalid entry for `%s': font file missing",
886                                                 fm->tfm_name);
887                     mp_warn(mp,s);
888                  }
889                 a += 2;
890             }|
891             */
892         }
893     }
894     if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
895         if (warn) {
896             snprintf(s,128, 
897                 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
898                  fm->tfm_name);
899                     mp_warn(mp,s);
900         }
901         a += 4;
902     }
903     if ((fm->slant != 0 || fm->extend != 0) &&
904         (is_truetype (fm))) {
905         if (warn) { 
906            snprintf(s,128, 
907                  "invalid entry for `%s': " 
908                  "SlantFont/ExtendFont can be used only with embedded T1 fonts",
909                  fm->tfm_name);
910                     mp_warn(mp,s);
911         }
912         a += 8;
913     }
914     if (abs (fm->slant) > 1000) {
915         if (warn) {
916             snprintf(s,128, 
917                 "invalid entry for `%s': too big value of SlantFont (%g)",
918                  fm->tfm_name, fm->slant / 1000.0);
919                     mp_warn(mp,s);
920         }
921         a += 16;
922     }
923     if (abs (fm->extend) > 2000) {
924         if (warn) {
925             snprintf(s,128, 
926                 "invalid entry for `%s': too big value of ExtendFont (%g)",
927                  fm->tfm_name, fm->extend / 1000.0);
928                     mp_warn(mp,s);
929         }
930         a += 32;
931     }
932     if (fm->pid != -1 &&
933         !(is_truetype (fm) && is_included (fm) &&
934           is_subsetted (fm) && !is_reencoded (fm))) {
935         if (warn) {
936             snprintf(s,128, 
937                 "invalid entry for `%s': "
938                  "PidEid can be used only with subsetted non-reencoded TrueType fonts",
939                  fm->tfm_name);
940                     mp_warn(mp,s);
941         }
942         a += 64;
943     }
944     return a;
945 }
946
947 @ returns true if s is one of the 14 std. font names; speed-trimmed. 
948
949 @c static boolean check_basefont (char *s) {
950     static const char *basefont_names[] = {
951         "Courier",              /* 0:7 */
952         "Courier-Bold",         /* 1:12 */
953         "Courier-Oblique",      /* 2:15 */
954         "Courier-BoldOblique",  /* 3:19 */
955         "Helvetica",            /* 4:9 */
956         "Helvetica-Bold",       /* 5:14 */
957         "Helvetica-Oblique",    /* 6:17 */
958         "Helvetica-BoldOblique",        /* 7:21 */
959         "Symbol",               /* 8:6 */
960         "Times-Roman",          /* 9:11 */
961         "Times-Bold",           /* 10:10 */
962         "Times-Italic",         /* 11:12 */
963         "Times-BoldItalic",     /* 12:16 */
964         "ZapfDingbats"          /* 13:12 */
965     };
966     static const int Index[] =
967         { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
968         -1, 3, -1, 7
969     };
970     const size_t n = strlen (s);
971     int k = -1;
972     if (n > 21)
973         return false;
974     if (n == 12) {              /* three names have length 12 */
975         switch (*s) {
976         case 'C':
977             k = 1;              /* Courier-Bold */
978             break;
979         case 'T':
980             k = 11;             /* Times-Italic */
981             break;
982         case 'Z':
983             k = 13;             /* ZapfDingbats */
984             break;
985         default:
986             return false;
987         }
988     } else
989         k = Index[n];
990     if (k > -1 && !strcmp (basefont_names[k], s))
991         return true;
992     return false;
993 };
994
995
996 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
997
998 @c static void fm_scan_line (MP mp) {
999     int a, b, c, j, u = 0, v = 0;
1000     float d;
1001     fm_entry *fm;
1002     char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1003     char *p, *q, *r, *s;
1004     char warn_s[128];
1005     switch (mp->ps->mitem->type) {
1006     case MAPFILE:
1007         p = fm_line;
1008         do {
1009             c = fm_getchar ();
1010             append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1011         }
1012         while (c != 10);
1013         *(--p) = '\0';
1014         r = fm_line;
1015         break;
1016     case MAPLINE:
1017         r = mp->ps->mitem->map_line;
1018         break;
1019     default:
1020         assert (0);
1021     }
1022     if (*r == '\0' || is_cfg_comment (*r))
1023         return;
1024     fm = new_fm_entry (mp);
1025     read_field (r, q, buf);
1026     set_field (tfm_name);
1027     p = r;
1028     read_field (r, q, buf);
1029     if (*buf != '<' && *buf != '"')
1030         set_field (ps_name);
1031     else
1032         r = p;                  /* unget the field */
1033     if (isdigit (*r)) {         /* font flags given */
1034         fm->flags = atoi (r);
1035         while (isdigit (*r))
1036             r++;
1037     }
1038     while (1) {                 /* loop through "specials", encoding, font file */
1039         skip (r, ' ');
1040         switch (*r) {
1041         case '\0':
1042             goto DONE;
1043         case '"':              /* opening quote */
1044             r++;
1045             u = v = 0;
1046             do {
1047                 skip (r, ' ');
1048                 if (sscanf (r, "%f %n", &d, &j) > 0) {
1049                     s = r + j;  /* jump behind number, eat also blanks, if any */
1050                     if (*(s - 1) == 'E' || *(s - 1) == 'e')
1051                         s--;    /* e. g. 0.5ExtendFont: \%f = 0.5E */
1052                     if (str_prefix (s, "SlantFont")) {
1053                         d *= 1000.0;    /* correct rounding also for neg. numbers */
1054                         fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1055                         r = s + strlen ("SlantFont");
1056                     } else if (str_prefix (s, "ExtendFont")) {
1057                         d *= 1000.0;
1058                         fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1059                         if (fm->extend == 1000)
1060                             fm->extend = 0;
1061                         r = s + strlen ("ExtendFont");
1062                     } else {    /* unknown name */
1063                         for (r = s; 
1064                              *r != ' ' && *r != '"' && *r != '\0'; 
1065                              r++); /* jump over name */
1066                         c = *r; /* remember char for temporary end of string */
1067                         *r = '\0';
1068                         snprintf(warn_s,128,
1069                             "invalid entry for `%s': unknown name `%s' ignored",
1070                              fm->tfm_name, s);
1071                         mp_warn(mp,warn_s);
1072                         *r = c;
1073                     }
1074                 } else
1075                     for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1076             }
1077             while (*r == ' ');
1078             if (*r == '"')      /* closing quote */
1079                 r++;
1080             else {
1081                 snprintf(warn_s,128,
1082                     "invalid entry for `%s': closing quote missing",
1083                      fm->tfm_name);
1084                 mp_warn(mp,warn_s);
1085                 goto bad_line;
1086             }
1087             break;
1088         case 'P':              /* handle cases for subfonts like 'PidEid=3,1' */
1089             if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1090                 fm->pid = a;
1091                 fm->eid = b;
1092                 r += c;
1093                 break;
1094             }
1095         default:               /* encoding or font file specification */
1096             a = b = 0;
1097             if (*r == '<') {
1098                 a = *r++;
1099                 if (*r == '<' || *r == '[')
1100                     b = *r++;
1101             }
1102             read_field (r, q, buf);
1103             /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1104             if (strlen (buf) > 4 && strcasecmp (strend (buf) - 4, ".enc") == 0) {
1105                 fm->encoding = mp_add_enc (mp, buf);
1106                 u = v = 0;      /* u, v used if intervening blank: "<< foo" */
1107             } else if (strlen (buf) > 0) {      /* file name given */
1108                 /* font file, formats:
1109                  * subsetting:    '<cmr10.pfa'
1110                  * no subsetting: '<<cmr10.pfa'
1111                  * no embedding:  'cmr10.pfa'
1112                  */
1113                 if (a == '<' || u == '<') {
1114                   set_included (fm);
1115                   if ((a == '<' && b == 0) || (a == 0 && v == 0))
1116                     set_subsetted (fm);
1117                   /* otherwise b == '<' (or '[') => no subsetting */
1118                 }
1119                 set_field (ff_name);
1120                 u = v = 0;
1121             } else {
1122                 u = a;
1123                 v = b;
1124             }
1125         }
1126     }
1127   DONE:
1128     if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1129         set_basefont (fm);
1130     if (is_fontfile (fm)
1131         && strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1132         set_truetype (fm);
1133     if (check_fm_entry (mp,fm, true) != 0)
1134         goto bad_line;
1135     /*
1136        Until here the map line has been completely scanned without errors;
1137        fm points to a valid, freshly filled-out |fm_entry| structure.
1138        Now follows the actual work of registering/deleting.
1139      */
1140     if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0)    /* if success */
1141         return;
1142   bad_line:
1143     delete_fm_entry (fm);
1144 }
1145
1146
1147 @c static void fm_read_info (MP mp) {
1148     char *n;
1149     char s[256];
1150     if (mp->ps->tfm_tree == NULL)
1151         create_avl_trees (mp);
1152     if (mp->ps->mitem->map_line == NULL)    /* nothing to do */
1153         return;
1154     mp->ps->mitem->lineno = 1;
1155     switch (mp->ps->mitem->type) {
1156     case MAPFILE:
1157         n = mp->ps->mitem->map_line;
1158         mp->ps->fm_file = mp_open_file(mp, n, "r", mp_filetype_fontmap);
1159         if (!mp->ps->fm_file) {
1160             snprintf(s,256,"cannot open font map file %s",n);
1161             mp_warn(mp,s);
1162         } else {
1163             int save_selector = mp->selector;
1164             mp_normalize_selector(mp);
1165             mp_print (mp, "{");
1166             mp_print (mp, n);
1167             while (!fm_eof ()) {
1168                 fm_scan_line (mp);
1169                 mp->ps->mitem->lineno++;
1170             }
1171             fm_close ();
1172             mp_print (mp,"}");
1173             mp->selector = save_selector;
1174             mp->ps->fm_file = NULL;
1175         }
1176         break;
1177     case MAPLINE:
1178         fm_scan_line (mp);
1179         break;
1180     default:
1181         assert (0);
1182     }
1183     mp->ps->mitem->map_line = NULL;         /* done with this line */
1184     return;
1185 }
1186
1187 @ @c 
1188 static scaled mp_round_xn_over_d (MP mp, scaled x, integer  n, integer d) {
1189   boolean positive; /* was |x>=0|? */
1190   unsigned int t,u; /* intermediate quantities */
1191   integer v; /* intermediate quantities */
1192   if ( x>=0 ) {
1193     positive=true;
1194   } else { 
1195     negate(x); positive=false;
1196   };
1197   t=(x % 0100000)*n;
1198   u=(x / 0100000)*n+(t / 0100000);
1199   v=(u % d)*0100000 + (t % 0100000);
1200   if ( u / d>=0100000 ) mp->arith_error=true;
1201   else u=0100000*(u / d) + (v / d);
1202   v = v % d;
1203   if ( 2*v >= d )
1204     u++;
1205   return ( positive ? u : -u );
1206 }
1207 static fm_entry *mk_ex_fm (MP mp, font_number f, fm_entry * basefm, int ex) {
1208     fm_entry *fm;
1209     integer e = basefm->extend;
1210     if (e == 0)
1211         e = 1000;
1212     fm = new_fm_entry (mp);
1213     fm->flags = basefm->flags;
1214     fm->encoding = basefm->encoding;
1215     fm->type = basefm->type;
1216     fm->slant = basefm->slant;
1217     fm->extend = mp_round_xn_over_d (mp, e, 1000 + ex, 1000); 
1218         /* modify ExtentFont to simulate expansion */
1219     if (fm->extend == 1000)
1220         fm->extend = 0;
1221     fm->tfm_name = mp_xstrdup (mp,mp->font_name[f]);
1222     if (basefm->ps_name != NULL)
1223         fm->ps_name = mp_xstrdup (mp,basefm->ps_name);
1224     fm->ff_name = mp_xstrdup (mp,basefm->ff_name);
1225     fm->ff_objnum = 0;
1226     fm->tfm_num = f;
1227     fm->tfm_avail = TFM_FOUND;
1228     assert (strcmp (fm->tfm_name, nontfm));
1229     return fm;
1230 }
1231
1232 @ @c static void init_fm (fm_entry * fm, font_number f) {
1233     if (fm->tfm_num == null_font ) {
1234         fm->tfm_num = f;
1235         fm->tfm_avail = TFM_FOUND;
1236     }
1237 }
1238
1239 @ @<Declarations@>=
1240 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1241
1242 @ @c 
1243 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1244     char *tfm;
1245     fm_entry *fm, *exfm;
1246     fm_entry tmp;
1247     int ai, e;
1248     if (mp->ps->tfm_tree == NULL)
1249         fm_read_info (mp);        /* only to read default map file */
1250     tfm = mp->font_name[f];
1251     assert (strcmp (tfm, nontfm));
1252     /* Look up for full <tfmname>[+-]<expand> */
1253     tmp.tfm_name = tfm;
1254     fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1255     if (fm != NULL) {
1256         init_fm (fm, f);
1257         return (fm_entry *) fm;
1258     }
1259     tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1260     if (tfm == NULL)            /* not an expanded font, nothing to do */
1261         return NULL;
1262
1263     tmp.tfm_name = tfm;
1264     fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1265     if (fm != NULL) {           /* found an entry with the base tfm name, e.g. cmr10 */
1266         return (fm_entry *) fm; /* font expansion uses the base font */
1267         /* the following code would be obsolete, as would be |mk_ex_fm| */
1268         if (!is_t1fontfile (fm) || !is_included (fm)) {
1269             char s[128];
1270             snprintf(s,128,
1271                 "font %s cannot be expanded (not an included Type1 font)", tfm);
1272             mp_warn(mp,s);
1273             return NULL;
1274         }
1275         exfm = mk_ex_fm (mp, f, fm, e);     /* copies all fields from fm except tfm name */
1276         init_fm (exfm, f);
1277         ai = avl_do_entry (mp, exfm, FM_DUPIGNORE);
1278         assert (ai == 0);
1279         return (fm_entry *) exfm;
1280     }
1281     return NULL;
1282 }
1283
1284 @  Early check whether a font file exists. Used e. g. for replacing fonts
1285    of embedded PDF files: Without font file, the font within the embedded
1286    PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1287    may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1288    only once per font file name + expansion parameter. This might help
1289    keeping speed, if many PDF pages with same fonts are to be embedded.
1290
1291    The |ff_tree| contains only font files, which are actually needed,
1292    so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1293
1294 @c 
1295 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1296     ff_entry *ff;
1297     ff_entry tmp;
1298     void **aa;
1299
1300     assert (fm->ff_name != NULL);
1301     tmp.ff_name = fm->ff_name;
1302     ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1303     if (ff == NULL) {           /* not yet in database */
1304         ff = new_ff_entry (mp);
1305         ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1306         ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1307         aa = avl_probe (mp->ps->ff_tree, ff);
1308         assert (aa != NULL);
1309     }
1310     return ff;
1311 }
1312
1313 @ Process map file given by its name or map line contents. Items not
1314 beginning with [+-=] flush default map file, if it has not yet been
1315 read. Leading blanks and blanks immediately following [+-=] are ignored.
1316
1317
1318 @c static void mp_process_map_item (MP mp, char *s, int type) {
1319     char *p;
1320     int mode;
1321     if (*s == ' ')
1322         s++;                    /* ignore leading blank */
1323     switch (*s) {
1324     case '+':                  /* +mapfile.map, +mapline */
1325         mode = FM_DUPIGNORE;    /* insert entry, if it is not duplicate */
1326         s++;
1327         break;
1328     case '=':                  /* =mapfile.map, =mapline */
1329         mode = FM_REPLACE;      /* try to replace earlier entry */
1330         s++;
1331         break;
1332     case '-':                  /* -mapfile.map, -mapline */
1333         mode = FM_DELETE;       /* try to delete entry */
1334         s++;
1335         break;
1336     default:
1337         mode = FM_DUPIGNORE;    /* like +, but also: */
1338         mp->ps->mitem->map_line = NULL;     /* flush default map file name */
1339     }
1340     if (*s == ' ')
1341         s++;                    /* ignore blank after [+-=] */
1342     p = s;                      /* map item starts here */
1343     switch (type) {
1344     case MAPFILE:              /* remove blank at end */
1345         while (*p != '\0' && *p != ' ')
1346             p++;
1347         *p = '\0';
1348         break;
1349     case MAPLINE:              /* blank at end allowed */
1350         break;
1351     default:
1352         assert (0);
1353     }
1354     if (mp->ps->mitem->map_line != NULL)    /* read default map file first */
1355         fm_read_info (mp);
1356     if (*s != '\0') {           /* only if real item to process */
1357         mp->ps->mitem->mode = mode;
1358         mp->ps->mitem->type = type;
1359         mp->ps->mitem->map_line = s;
1360         fm_read_info (mp);
1361     }
1362 }
1363
1364 @ @<Exported function headers@>=
1365 void mp_map_file (MP mp, str_number t);
1366 void mp_map_line (MP mp, str_number t);
1367 void mp_init_map_file (MP mp, int is_troff);
1368
1369 @ @c 
1370 void mp_map_file (MP mp, str_number t) {
1371   char *s = mp_xstrdup(mp,mp_str (mp,t));
1372   mp_process_map_item (mp, s, MAPFILE);
1373   mp_xfree (s);
1374 }
1375 void mp_map_line (MP mp, str_number t) {
1376   char *s = mp_xstrdup(mp,mp_str (mp,t));
1377   mp_process_map_item (mp, s, MAPLINE);
1378   mp_xfree (s);
1379 }
1380
1381
1382 @c void mp_init_map_file (MP mp, int is_troff) {
1383     char *r;
1384     mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1385     mp->ps->mitem->mode = FM_DUPIGNORE;
1386     mp->ps->mitem->type = MAPFILE;
1387     mp->ps->mitem->map_line = NULL;
1388     r = (mp->find_file)("mpost.map", "rb", mp_filetype_fontmap);
1389     if (r != NULL) {
1390       mp_xfree(r);
1391       mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1392     } else {
1393       if (is_troff) {
1394              mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1395       } else {
1396              mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1397       }
1398     }
1399 }
1400
1401 @ @<Dealloc variables@>=
1402 if (mp->ps->mitem!=NULL) {
1403   mp_xfree(mp->ps->mitem->map_line);
1404   mp_xfree(mp->ps->mitem);
1405 }
1406
1407 @ cleaning up... 
1408
1409 @c
1410 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1411     fm_entry *fm;
1412     assert(pb==NULL);
1413     fm = (fm_entry *) pa;
1414     if (!has_pslink (fm))
1415         delete_fm_entry (fm);
1416     else
1417         unset_tfmlink (fm);
1418 }
1419 static void destroy_fm_entry_ps (void *pa, void *pb) {
1420     fm_entry *fm;
1421     assert(pb==NULL);
1422     fm = (fm_entry *) pa;
1423     if (!has_tfmlink (fm))
1424         delete_fm_entry (fm);
1425     else
1426         unset_pslink (fm);
1427 }
1428 static void destroy_ff_entry (void *pa, void *pb) {
1429     ff_entry *ff;
1430     assert(pb==NULL);
1431     ff = (ff_entry *) pa;
1432     delete_ff_entry (ff);
1433
1434
1435 @ @<Declarations@>=
1436 static void fm_free (MP mp);
1437
1438 @ @c
1439 static void fm_free (MP mp) {
1440     if (mp->ps->tfm_tree != NULL)
1441         avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1442     if (mp->ps->ps_tree != NULL)
1443         avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1444     if (mp->ps->ff_tree != NULL)
1445         avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1446 }
1447
1448 @* \[44c] Helper functions for Type1 fonts.
1449
1450 @<Types...@>=
1451 typedef char char_entry;
1452 typedef unsigned char  Byte;
1453 typedef Byte  Bytef;
1454
1455 @ @<Glob...@>=
1456 char_entry *char_ptr, *char_array;
1457 size_t char_limit;
1458 char *job_id_string;
1459
1460 @ @<Set initial...@>=
1461 mp->ps->char_array = NULL;
1462 mp->ps->job_id_string = NULL;
1463
1464
1465 @d SMALL_ARRAY_SIZE    256
1466 @d Z_NULL  0  
1467
1468 @c 
1469 void mp_set_job_id (MP mp) {
1470     char *name_string, *format_string, *s;
1471     size_t slen;
1472     int i;
1473     if (mp->ps->job_id_string != NULL)
1474        return;
1475     if ( mp->job_name==NULL )
1476        mp->job_name = mp_xstrdup(mp,"mpout");
1477     name_string = mp_xstrdup (mp,mp->job_name);
1478     format_string = mp_xstrdup (mp,mp->mem_ident);
1479     slen = SMALL_BUF_SIZE +
1480         strlen (name_string) +
1481         strlen (format_string);
1482     s = mp_xmalloc (mp,slen, sizeof (char));
1483     i = snprintf (s, slen,
1484                   "%.4d/%.2d/%.2d %.2d:%.2d %s %s",
1485                   (mp->internal[mp_year]>>16),
1486                   (mp->internal[mp_month]>>16), 
1487                   (mp->internal[mp_day]>>16), 
1488                   (mp->internal[mp_time]>>16) / 60, 
1489                   (mp->internal[mp_time]>>16) % 60,
1490                   name_string, format_string);
1491     mp->ps->job_id_string = mp_xstrdup (mp,s);
1492     mp_xfree (s);
1493     mp_xfree (name_string);
1494     mp_xfree (format_string);
1495 }
1496 static void fnstr_append (MP mp, const char *s) {
1497     size_t l = strlen (s) + 1;
1498     alloc_array (char, l, SMALL_ARRAY_SIZE);
1499     strcat (mp->ps->char_ptr, s);
1500     mp->ps->char_ptr = strend (mp->ps->char_ptr);
1501 }
1502
1503 @ @<Exported function headers@>=
1504 void mp_set_job_id (MP mp) ;
1505
1506 @ @<Dealloc variables@>=
1507 mp_xfree(mp->ps->job_id_string);
1508
1509 @ this is not really a true crc32, but it should be just enough to keep
1510   subsets prefixes somewhat disjunct
1511
1512 @c
1513 static unsigned long crc32 (int oldcrc, const Byte *buf, int len) {
1514   unsigned long ret = 0;
1515   int i;
1516   if (oldcrc==0)
1517         ret = (23<<24)+(45<<16)+(67<<8)+89;
1518   else 
1519       for (i=0;i<len;i++)
1520           ret = (ret<<2)+buf[i];
1521   return ret;
1522 }
1523 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1524   integer b; /* |char_base[f]| */
1525   b=mp->char_base[f];
1526   if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1527     return true;
1528   else
1529     return false;
1530 }
1531
1532 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, int tex_font)
1533 {
1534     char tag[7];
1535     unsigned long crc;
1536     int i;
1537     size_t l ;
1538     if (mp->ps->job_id_string ==NULL)
1539       mp_fatal_error(mp, "no job id!");
1540     l = strlen (mp->ps->job_id_string) + 1;
1541     
1542     alloc_array (char, l, SMALL_ARRAY_SIZE);
1543     strcpy (mp->ps->char_array, mp->ps->job_id_string);
1544     mp->ps->char_ptr = strend (mp->ps->char_array);
1545     if (fm_cur->tfm_name != NULL) {
1546         fnstr_append (mp," TFM name: ");
1547         fnstr_append (mp,fm_cur->tfm_name);
1548     }
1549     fnstr_append (mp," PS name: ");
1550     if (fm_cur->ps_name != NULL)
1551         fnstr_append (mp,fm_cur->ps_name);
1552     fnstr_append (mp," Encoding: ");
1553     if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1554         fnstr_append (mp,(fm_cur->encoding)->file_name);
1555     else
1556         fnstr_append (mp,"built-in");
1557     fnstr_append (mp," CharSet: ");
1558     for (i = 0; i < 256; i++)
1559         if (mp_char_marked (mp,tex_font, i) && glyph_names[i] != notdef) {
1560                         if (glyph_names[i]!=NULL) {
1561                           fnstr_append (mp,"/");
1562                           fnstr_append (mp,glyph_names[i]);
1563                         }
1564         }
1565     if (fm_cur->charset != NULL) {
1566         fnstr_append (mp," Extra CharSet: ");
1567         fnstr_append (mp, fm_cur->charset);
1568     }
1569     crc = crc32 (0L, Z_NULL, 0);
1570     crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1571     /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1572      * there are 26 uppercase chars ==> each char represents a number in range
1573      * |0..25|. The maximal number that can be represented by the tag is
1574      * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1575      * of the CRC must be dropped out.
1576      */
1577     for (i = 0; i < 6; i++) {
1578         tag[i] = 'A' + crc % 26;
1579         crc /= 26;
1580     }
1581     tag[6] = 0;
1582     fm_cur->subset_tag = mp_xstrdup (mp,tag);
1583 }
1584
1585
1586
1587
1588 @d external_enc()      (fm_cur->encoding)->glyph_names
1589 @d is_used_char(c)     mp_char_marked (mp, tex_font, c)
1590 @d end_last_eexec_line() 
1591     mp->ps->hexline_length = HEXLINE_WIDTH;
1592     end_hexline(mp); 
1593     mp->ps->t1_eexec_encrypt = false
1594 @d t1_log(s)           mp_print(mp,(char *)s)
1595 @d t1_putchar(c)       fputc(c, mp->ps_file)
1596 @d embed_all_glyphs(tex_font)  false
1597 @d t1_char(c)          c
1598 @d extra_charset()     mp->ps->dvips_extra_charset
1599 @d update_subset_tag()
1600 @d fixedcontent        true
1601
1602 @<Glob...@>=
1603 #define PRINTF_BUF_SIZE     1024
1604 char *dvips_extra_charset;
1605 char *cur_enc_name;
1606 unsigned char *grid;
1607 char *ext_glyph_names[256];
1608 char print_buf[PRINTF_BUF_SIZE];
1609
1610 @ @<Set initial ...@>=
1611 mp->ps->dvips_extra_charset=NULL;
1612
1613
1614 @d t1_getchar()    fgetc(mp->ps->t1_file)
1615 @d t1_ungetchar(c) ungetc(c, mp->ps->t1_file)
1616 @d t1_eof()        feof(mp->ps->t1_file)
1617 @d t1_close()      fclose(mp->ps->t1_file)
1618 @d valid_code(c)   (c >= 0 && c < 256)
1619
1620 @<Static variables in the outer block@>=
1621 static const char *standard_glyph_names[256] =
1622     { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1623     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1624     notdef, notdef, notdef, notdef, notdef, notdef,
1625     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1626     "space", "exclam", "quotedbl", "numbersign",
1627     "dollar", "percent", "ampersand", "quoteright", "parenleft",
1628     "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1629     "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1630     "eight", "nine", "colon", "semicolon", "less",
1631     "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1632     "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1633     "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1634     "backslash", "bracketright", "asciicircum", "underscore",
1635     "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1636     "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1637     "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1638     notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1639     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1640     notdef, notdef, notdef, notdef, notdef, notdef,
1641     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1642     notdef, notdef, notdef, "exclamdown", "cent",
1643     "sterling", "fraction", "yen", "florin", "section", "currency",
1644     "quotesingle", "quotedblleft", "guillemotleft",
1645     "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1646     "dagger", "daggerdbl", "periodcentered", notdef,
1647     "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1648     "quotedblright", "guillemotright", "ellipsis", "perthousand",
1649     notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1650     "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1651     "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1652     notdef, notdef, notdef, notdef, notdef, notdef,
1653     notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1654     notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1655     notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1656     notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1657     notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1658     "germandbls", notdef, notdef, notdef, notdef };
1659 static const char charstringname[] = "/CharStrings";
1660
1661 @ @<Glob...@>=
1662 char **t1_glyph_names;
1663 char *t1_builtin_glyph_names[256];
1664 char charsetstr[0x4000];
1665 boolean read_encoding_only;
1666 int t1_encoding;
1667
1668 @ @c
1669 #define T1_BUF_SIZE   0x10
1670
1671 #define CS_HSTEM            1
1672 #define CS_VSTEM            3
1673 #define CS_VMOVETO          4
1674 #define CS_RLINETO          5
1675 #define CS_HLINETO          6
1676 #define CS_VLINETO          7
1677 #define CS_RRCURVETO        8
1678 #define CS_CLOSEPATH        9
1679 #define CS_CALLSUBR         10
1680 #define CS_RETURN           11
1681 #define CS_ESCAPE           12
1682 #define CS_HSBW             13
1683 #define CS_ENDCHAR          14
1684 #define CS_RMOVETO          21
1685 #define CS_HMOVETO          22
1686 #define CS_VHCURVETO        30
1687 #define CS_HVCURVETO        31
1688 #define CS_1BYTE_MAX        (CS_HVCURVETO + 1)
1689
1690 #define CS_DOTSECTION       CS_1BYTE_MAX + 0
1691 #define CS_VSTEM3           CS_1BYTE_MAX + 1
1692 #define CS_HSTEM3           CS_1BYTE_MAX + 2
1693 #define CS_SEAC             CS_1BYTE_MAX + 6
1694 #define CS_SBW              CS_1BYTE_MAX + 7
1695 #define CS_DIV              CS_1BYTE_MAX + 12
1696 #define CS_CALLOTHERSUBR    CS_1BYTE_MAX + 16
1697 #define CS_POP              CS_1BYTE_MAX + 17
1698 #define CS_SETCURRENTPOINT  CS_1BYTE_MAX + 33
1699 #define CS_2BYTE_MAX        (CS_SETCURRENTPOINT + 1)
1700 #define CS_MAX              CS_2BYTE_MAX
1701
1702 @ @<Types...@>=
1703 typedef unsigned char byte;
1704 typedef struct {
1705     byte nargs;                 /* number of arguments */
1706     boolean bottom;             /* take arguments from bottom of stack? */
1707     boolean clear;              /* clear stack? */
1708     boolean valid;
1709 } cc_entry;                     /* CharString Command */
1710 typedef struct {
1711     char *glyph_name;                 /* glyph name (or notdef for Subrs entry) */
1712     byte *data;
1713     unsigned short len;         /* length of the whole string */
1714     unsigned short cslen;       /* length of the encoded part of the string */
1715     boolean is_used;
1716     boolean valid;
1717 } cs_entry;
1718
1719 @ @<Glob...@>=
1720 unsigned short t1_dr, t1_er;
1721 unsigned short t1_c1, t1_c2;
1722 unsigned short t1_cslen;
1723 short t1_lenIV;
1724
1725 @ @<Set initial...@>=
1726 mp->ps->t1_c1 = 52845; 
1727 mp->ps->t1_c2 = 22719;
1728
1729 @ @<Types...@>=
1730 typedef char t1_line_entry;
1731 typedef char t1_buf_entry;
1732
1733 @ @<Glob...@>=
1734 t1_line_entry *t1_line_ptr, *t1_line_array;
1735 size_t t1_line_limit;
1736 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1737 size_t t1_buf_limit;
1738 int cs_start;
1739 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1740 char *cs_dict_start, *cs_dict_end;
1741 int cs_count, cs_size, cs_size_pos;
1742 cs_entry *subr_tab;
1743 char *subr_array_start, *subr_array_end;
1744 int subr_max, subr_size, subr_size_pos;
1745
1746 @ @<Set initial...@>=
1747 mp->ps->t1_line_array = NULL;
1748 mp->ps->t1_buf_array = NULL;
1749
1750
1751  This list contains the begin/end tokens commonly used in the 
1752  /Subrs array of a Type 1 font.                                
1753
1754 @<Static variables in the outer block@>=
1755 static const char *cs_token_pairs_list[][2] = {
1756     {" RD", "NP"},
1757     {" -|", "|"},
1758     {" RD", "noaccess put"},
1759     {" -|", "noaccess put"},
1760     {NULL, NULL}
1761 };
1762
1763 @ @<Glob...@>=
1764 const char **cs_token_pair;
1765 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1766 int t1_in_eexec;  /* 0 before eexec-encrypted, 1 during, 2 after */
1767 long t1_block_length;
1768 int last_hexbyte;
1769 FILE *t1_file;
1770 int hexline_length;
1771
1772
1773 @d HEXLINE_WIDTH 64
1774
1775 @<Set initial ...@>=
1776 mp->ps->hexline_length = HEXLINE_WIDTH;
1777
1778
1779 @d t1_prefix(s)        str_prefix(mp->ps->t1_line_array, s)
1780 @d t1_buf_prefix(s)    str_prefix(mp->ps->t1_buf_array, s)
1781 @d t1_suffix(s)        str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1782 @d t1_buf_suffix(s)    str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1783 @d t1_charstrings()    strstr(mp->ps->t1_line_array, charstringname)
1784 @d t1_subrs()          t1_prefix("/Subrs")
1785 @d t1_end_eexec()      t1_suffix("mark currentfile closefile")
1786 @d t1_cleartomark()    t1_prefix("cleartomark")
1787
1788 @d isdigit(A) ((A)>='0'&&(A)<='9')
1789
1790 @c
1791 static void end_hexline (MP mp) {
1792     if (mp->ps->hexline_length == HEXLINE_WIDTH) {
1793         fputs ("\n", mp->ps_file);
1794         mp->ps->hexline_length = 0;
1795     }
1796 }
1797 static void t1_check_pfa (MP mp) {
1798     const int c = t1_getchar ();
1799     mp->ps->t1_pfa = (c != 128) ? true : false;
1800     t1_ungetchar (c);
1801 }
1802 static int t1_getbyte (MP mp)
1803 {
1804     int c = t1_getchar ();
1805     if (mp->ps->t1_pfa)
1806         return c;
1807     if (mp->ps->t1_block_length == 0) {
1808         if (c != 128)
1809          mp_fatal_error (mp, "invalid marker");
1810         c = t1_getchar ();
1811         if (c == 3) {
1812             while (!t1_eof ())
1813                 t1_getchar ();
1814             return EOF;
1815         }
1816         mp->ps->t1_block_length = t1_getchar () & 0xff;
1817         mp->ps->t1_block_length |= (t1_getchar () & 0xff) << 8;
1818         mp->ps->t1_block_length |= (t1_getchar () & 0xff) << 16;
1819         mp->ps->t1_block_length |= (t1_getchar () & 0xff) << 24;
1820         c = t1_getchar ();
1821     }
1822     mp->ps->t1_block_length--;
1823     return c;
1824 }
1825 static int hexval (int c) {
1826     if (c >= 'A' && c <= 'F')
1827         return c - 'A' + 10;
1828     else if (c >= 'a' && c <= 'f')
1829         return c - 'a' + 10;
1830     else if (c >= '0' && c <= '9')
1831         return c - '0';
1832     else
1833         return -1;
1834 }
1835 static byte edecrypt (MP mp, byte cipher) {
1836     byte plain;
1837     if (mp->ps->t1_pfa) {
1838         while (cipher == 10 || cipher == 13)
1839             cipher = t1_getbyte (mp);
1840         mp->ps->last_hexbyte = cipher = (hexval (cipher) << 4) + hexval (t1_getbyte (mp));
1841     }
1842     plain = (cipher ^ (mp->ps->t1_dr >> 8));
1843     mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1844     return plain;
1845 }
1846 static byte cdecrypt (MP mp, byte cipher, unsigned short *cr)
1847 {
1848     const byte plain = (cipher ^ (*cr >> 8));
1849     *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1850     return plain;
1851 }
1852 static byte eencrypt (MP mp, byte plain)
1853 {
1854     const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1855     mp->ps->t1_er = (cipher + mp->ps->t1_er) * mp->ps->t1_c1 + mp->ps->t1_c2;
1856     return cipher;
1857 }
1858
1859 static byte cencrypt (MP mp, byte plain, unsigned short *cr)
1860 {
1861     const byte cipher = (plain ^ (*cr >> 8));
1862     *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1863     return cipher;
1864 }
1865
1866 static char *eol (char *s) {
1867     char *p = strend (s);
1868     if (p - s > 1 && p[-1] != 10) {
1869         *p++ = 10;
1870         *p = 0;
1871     }
1872     return p;
1873 }
1874 static float t1_scan_num (MP mp, char *p, char **r)
1875 {
1876     float f;
1877     char s[128];
1878     skip (p, ' ');
1879     if (sscanf (p, "%g", &f) != 1) {
1880         remove_eol (p, mp->ps->t1_line_array); 
1881             snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1882         mp_fatal_error(mp,s);
1883     }
1884     if (r != NULL) {
1885         for (; isdigit (*p) || *p == '.' ||
1886              *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1887         *r = p;
1888     }
1889     return f;
1890 }
1891
1892 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1893                            const char *s)
1894 {
1895     const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1896     if (*s1 == 10)
1897         s1--;
1898     while (s1 >= begin_buf && s2 >= s) {
1899         if (*s1-- != *s2--)
1900             return false;
1901     }
1902     return s2 < s;
1903 }
1904
1905 @
1906
1907 @d alloc_array(T, n, s) do {
1908     if (mp->ps->T##_array == NULL) {
1909         mp->ps->T##_limit = (s);
1910         if ((unsigned)(n) > mp->ps->T##_limit)
1911             mp->ps->T##_limit = (n);
1912         mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
1913         mp->ps->T##_ptr = mp->ps->T##_array;
1914     }
1915     else if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
1916         size_t last_ptr_index;
1917         last_ptr_index = mp->ps->T##_ptr - mp->ps->T##_array;
1918         mp->ps->T##_limit *= 2;
1919         if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
1920             mp->ps->T##_limit = mp->ps->T##_ptr - mp->ps->T##_array + (n);
1921         mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
1922         mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
1923     }
1924 } while (0)
1925
1926 @d out_eexec_char(A)      t1_outhex(mp,(A))
1927  
1928 @c
1929 static void t1_outhex (MP mp, byte b)
1930 {
1931     static char *hexdigits = "0123456789ABCDEF";
1932     t1_putchar (hexdigits[b / 16]);
1933     t1_putchar (hexdigits[b % 16]);
1934     mp->ps->hexline_length += 2;
1935     end_hexline (mp);
1936 }
1937 static void t1_getline (MP mp) {
1938     int c, l, eexec_scan;
1939     char *p;
1940     static const char eexec_str[] = "currentfile eexec";
1941     static int eexec_len = 17;  /* |strlen(eexec_str)| */
1942   RESTART:
1943     if (t1_eof ())
1944         mp_fatal_error (mp,"unexpected end of file");
1945     mp->ps->t1_line_ptr = mp->ps->t1_line_array;
1946     alloc_array (t1_line, 1, T1_BUF_SIZE);
1947     mp->ps->t1_cslen = 0;
1948     eexec_scan = 0;
1949     c = t1_getbyte (mp);
1950     if (c == EOF)
1951         goto EXIT;
1952     while (!t1_eof ()) {
1953         if (mp->ps->t1_in_eexec == 1)
1954             c = edecrypt (mp,c);
1955         alloc_array (t1_line, 1, T1_BUF_SIZE);
1956         append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
1957         if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
1958             if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
1959                 eexec_scan++;
1960             else
1961                 eexec_scan = -1;
1962         }
1963         if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
1964             break;
1965         if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 && 
1966             (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
1967             (t1_suffix (" RD ") || t1_suffix (" -| "))) {
1968             p = mp->ps->t1_line_ptr - 5;
1969             while (*p != ' ')
1970                 p--;
1971             mp->ps->t1_cslen = l = t1_scan_num (mp, p + 1, 0);
1972             mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;     
1973                   /* |mp->ps->cs_start| is an index now */
1974             alloc_array (t1_line, l, T1_BUF_SIZE);
1975             while (l-- > 0)
1976                 *mp->ps->t1_line_ptr++ = edecrypt (mp,t1_getbyte (mp));
1977         }
1978         c = t1_getbyte (mp);
1979     }
1980     alloc_array (t1_line, 2, T1_BUF_SIZE);      /* |append_eol| can append 2 chars */
1981     append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
1982     if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
1983         goto RESTART;
1984     if (eexec_scan == eexec_len)
1985         mp->ps->t1_in_eexec = 1;
1986   EXIT:
1987     /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
1988     mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
1989     alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
1990 }
1991
1992 static void t1_putline (MP mp)
1993 {
1994     char *p = mp->ps->t1_line_array;
1995     if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
1996         return;
1997     if (mp->ps->t1_eexec_encrypt) {
1998         while (p < mp->ps->t1_line_ptr)
1999             out_eexec_char (eencrypt (mp,*p++));
2000     } else {
2001         while (p < mp->ps->t1_line_ptr)
2002             t1_putchar (*p++);
2003         }
2004 }
2005
2006 static void t1_puts (MP mp, const char *s)
2007 {
2008     if (s != mp->ps->t1_line_array)
2009         strcpy (mp->ps->t1_line_array, s);
2010     mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2011     t1_putline (mp);
2012 }
2013
2014 static void t1_printf (MP mp, const char *fmt, ...)
2015 {
2016     va_list args;
2017     va_start (args, fmt);
2018     vsprintf (mp->ps->t1_line_array, fmt, args);
2019     t1_puts (mp,mp->ps->t1_line_array);
2020     va_end (args);
2021 }
2022
2023 static void t1_init_params (MP mp, char *open_name_prefix,
2024                            char *cur_file_name) {
2025   if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2026     t1_log (open_name_prefix);
2027     t1_log (cur_file_name);
2028   }
2029     mp->ps->t1_lenIV = 4;
2030     mp->ps->t1_dr = 55665;
2031     mp->ps->t1_er = 55665;
2032     mp->ps->t1_in_eexec = 0;
2033     mp->ps->t1_cs = false;
2034     mp->ps->t1_scan = true;
2035     mp->ps->t1_synthetic = false;
2036     mp->ps->t1_eexec_encrypt = false;
2037     mp->ps->t1_block_length = 0;
2038     t1_check_pfa (mp);
2039 }
2040 static void  t1_close_font_file (MP mp, const char *close_name_suffix) {
2041   if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2042     t1_log (close_name_suffix);
2043   }
2044   t1_close ();
2045 }
2046
2047 static void  t1_check_block_len (MP mp, boolean decrypt) {
2048     int l, c;
2049     char s[128];
2050     if (mp->ps->t1_block_length == 0)
2051         return;
2052     c = t1_getbyte (mp);
2053     if (decrypt)
2054         c = edecrypt (mp,c);
2055     l = mp->ps->t1_block_length;
2056     if (!(l == 0 && (c == 10 || c == 13))) {
2057         snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2058         mp_warn(mp,s);
2059         while (l-- > 0)
2060           t1_getbyte (mp);
2061     }
2062 }
2063 static void  t1_start_eexec (MP mp, fm_entry *fm_cur) {
2064     int i;
2065     if (!mp->ps->t1_pfa)
2066      t1_check_block_len (mp, false);
2067     for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2068       edecrypt (mp, t1_getbyte (mp));
2069       *mp->ps->t1_line_ptr++ = 0;
2070     }
2071     mp->ps->t1_eexec_encrypt = true;
2072         if (!mp->ps->read_encoding_only)
2073           if (is_included (fm_cur))
2074         t1_putline (mp);          /* to put the first four bytes */
2075 }
2076 static void  t1_stop_eexec (MP mp) {
2077     int c;
2078     end_last_eexec_line ();
2079     if (!mp->ps->t1_pfa)
2080       t1_check_block_len (mp,true);
2081     else {
2082         c = edecrypt (mp, t1_getbyte (mp));
2083         if (!(c == 10 || c == 13)) {
2084            if (mp->ps->last_hexbyte == 0)
2085               t1_puts (mp,"00");
2086            else
2087               mp_warn (mp,"unexpected data after eexec");
2088         }
2089     }
2090     mp->ps->t1_cs = false;
2091     mp->ps->t1_in_eexec = 2;
2092 }
2093 static void  t1_modify_fm (MP mp) {
2094   mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2095 }
2096
2097 static void  t1_modify_italic (MP mp) {
2098   mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2099 }
2100
2101 @ @<Types...@>=
2102 typedef struct {
2103     const char *pdfname;
2104     const char *t1name;
2105     float value;
2106     boolean valid;
2107 } key_entry;
2108
2109 @
2110 @d FONT_KEYS_NUM  11
2111
2112 @<Declarations@>=
2113 static key_entry font_keys[FONT_KEYS_NUM] = {
2114     {"Ascent", "Ascender", 0, false},
2115     {"CapHeight", "CapHeight", 0, false},
2116     {"Descent", "Descender", 0, false},
2117     {"FontName", "FontName", 0, false},
2118     {"ItalicAngle", "ItalicAngle", 0, false},
2119     {"StemV", "StdVW", 0, false},
2120     {"XHeight", "XHeight", 0, false},
2121     {"FontBBox", "FontBBox", 0, false},
2122     {"", "", 0, false},
2123     {"", "", 0, false},
2124     {"", "", 0, false}
2125 };
2126
2127
2128
2129 @d ASCENT_CODE         0
2130 @d CAPHEIGHT_CODE      1
2131 @d DESCENT_CODE        2
2132 @d FONTNAME_CODE       3
2133 @d ITALIC_ANGLE_CODE   4
2134 @d STEMV_CODE          5
2135 @d XHEIGHT_CODE        6
2136 @d FONTBBOX1_CODE      7
2137 @d FONTBBOX2_CODE      8
2138 @d FONTBBOX3_CODE      9
2139 @d FONTBBOX4_CODE      10
2140 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2141
2142 @c
2143 static void  t1_scan_keys (MP mp, int tex_font,fm_entry *fm_cur) {
2144     int i, k;
2145     char *p, *r;
2146     key_entry *key;
2147     if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2148         if (t1_prefix ("/FontMatrix")) {
2149             t1_modify_fm (mp);
2150             return;
2151         }
2152         if (t1_prefix ("/ItalicAngle")) {
2153             t1_modify_italic (mp);
2154             return;
2155         }
2156     }
2157     if (t1_prefix ("/FontType")) {
2158         p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2159         if ((i = t1_scan_num (mp,p, 0)) != 1) {
2160             char s[128];
2161             snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2162             mp_fatal_error(mp,s);
2163         }
2164         return;
2165     }
2166     for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2167         if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2168             break;
2169     if (key - font_keys == MAX_KEY_CODE)
2170         return;
2171     key->valid = true;
2172     p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2173     skip (p, ' ');
2174     if ((k = key - font_keys) == FONTNAME_CODE) {
2175         if (*p != '/') {
2176           char s[128];
2177           remove_eol (p, mp->ps->t1_line_array);
2178           snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2179           mp_fatal_error(mp,s);
2180         }
2181         r = ++p;                /* skip the slash */
2182         if (is_included (fm_cur)) {
2183           /* save the fontname */
2184           strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2185           for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2186           mp->ps->fontname_buf[i]=0;
2187           
2188           if(is_subsetted (fm_cur)) {
2189             if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2190               make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2191             else
2192               make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2193
2194             alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1), 
2195                          T1_BUF_SIZE);
2196             strncpy (r, fm_cur->subset_tag , 6);
2197             *(r+6) = '-';
2198             strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2199             mp->ps->t1_line_ptr = eol (r);
2200           } else {
2201             /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2202             /*|*q = 0;|*/
2203             mp->ps->t1_line_ptr = eol (r);
2204           }
2205         }
2206         return;
2207     }
2208     if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2209         && (*p == '[' || *p == '{'))
2210         p++;
2211     if (k == FONTBBOX1_CODE) {
2212         for (i = 0; i < 4; i++) {
2213             key[i].value = t1_scan_num (mp, p, &r);
2214             p = r;
2215         }
2216         return;
2217     }
2218     key->value = t1_scan_num (mp, p, 0);
2219 }
2220 static void  t1_scan_param (MP mp, int tex_font,fm_entry *fm_cur)
2221 {
2222     static const char *lenIV = "/lenIV";
2223     if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2224         return;
2225     if (t1_prefix (lenIV)) {
2226         mp->ps->t1_lenIV = t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2227         return;
2228     }
2229     t1_scan_keys (mp, tex_font,fm_cur);
2230 }
2231 static void  copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2232     if (glyph_names[b] != notdef) {
2233         mp_xfree (glyph_names[b]);
2234         glyph_names[b] = (char *) notdef;
2235     }
2236     if (glyph_names[a] != notdef) {
2237         glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2238     }
2239 }
2240 static void  t1_builtin_enc (MP mp) {
2241     int i, a, b, c, counter = 0;
2242     char *r, *p;
2243     /*
2244      * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2245      */
2246     if (t1_suffix ("def")) {    /* predefined encoding */
2247         sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2248         if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2249             for (i = 0; i < 256; i++)
2250                 if (standard_glyph_names[i] == notdef)
2251                     mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2252                 else
2253                     mp->ps->t1_builtin_glyph_names[i] =
2254                         mp_xstrdup (mp,standard_glyph_names[i]);
2255             mp->ps->t1_encoding = ENC_STANDARD;
2256         } else {
2257             char s[128];
2258             snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2259                         mp->ps->t1_buf_array);
2260             mp_fatal_error(mp,s);
2261         }
2262         return;
2263     } else
2264         mp->ps->t1_encoding = ENC_BUILTIN;
2265     /*
2266      * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2267      * not a predefined encoding
2268      *
2269      * We have two possible forms of Encoding vector. The first case is
2270      *
2271      *     /Encoding [/a /b /c...] readonly def
2272      *
2273      * and the second case can look like
2274      *
2275      *     /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2276      *     dup 0 /x put
2277      *     dup 1 /y put
2278      *     ...
2279      *     readonly def
2280      */
2281     for (i = 0; i < 256; i++)
2282         mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2283     if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) {        /* the first case */
2284         r = strchr (mp->ps->t1_line_array, '[') + 1;
2285         skip (r, ' ');
2286         for (;;) {
2287             while (*r == '/') {
2288                 for (p = mp->ps->t1_buf_array, r++;
2289                      *r != 32 && *r != 10 && *r != ']' && *r != '/';
2290                      *p++ = *r++);
2291                 *p = 0;
2292                 skip (r, ' ');
2293                 if (counter > 255) {
2294                    mp_fatal_error
2295                         (mp, "encoding vector contains more than 256 names");
2296                 }
2297                 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2298                   mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2299                 counter++;
2300             }
2301             if (*r != 10 && *r != '%') {
2302                 if (str_prefix (r, "] def")
2303                     || str_prefix (r, "] readonly def"))
2304                     break;
2305                 else {
2306                     char s[128];
2307                     remove_eol (r, mp->ps->t1_line_array);
2308                     snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2309                                     mp->ps->t1_line_array);
2310                     mp_fatal_error(mp,s);
2311                 }
2312             }
2313             t1_getline (mp);
2314             r = mp->ps->t1_line_array;
2315         }
2316     } else {                    /* the second case */
2317         p = strchr (mp->ps->t1_line_array, 10);
2318         for (;;) {
2319             if (*p == 10) {
2320                 t1_getline (mp);
2321                 p = mp->ps->t1_line_array;
2322             }
2323             /*
2324                check for `dup <index> <glyph> put'
2325              */
2326             if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2327                 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2328                 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2329                     mp->ps->t1_builtin_glyph_names[i] = 
2330                       mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2331                 p = strstr (p, " put") + strlen (" put");
2332                 skip (p, ' ');
2333             }
2334             /*
2335                check for `dup dup <to> exch <from> get put'
2336              */
2337             else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2338                      && valid_code (a) && valid_code (b)) {
2339                 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2340                 p = strstr (p, " get put") + strlen (" get put");
2341                 skip (p, ' ');
2342             }
2343             /*
2344                check for `dup dup <from> <size> getinterval <to> exch putinterval'
2345              */
2346             else if (sscanf
2347                      (p, "dup dup %i %i getinterval %i exch putinterval",
2348                       &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2349                      && valid_code (c)) {
2350                 for (i = 0; i < c; i++)
2351                     copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2352                 p = strstr (p, " putinterval") + strlen (" putinterval");
2353                 skip (p, ' ');
2354             }
2355             /*
2356                check for `def' or `readonly def'
2357              */
2358             else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2359                      && strcmp (p, "def\n") == 0)
2360                 return;
2361             /*
2362                skip an unrecognizable word
2363              */
2364             else {
2365                 while (*p != ' ' && *p != 10)
2366                     p++;
2367                 skip (p, ' ');
2368             }
2369         }
2370     }
2371 }
2372
2373 static void  t1_check_end (MP mp) {
2374     if (t1_eof ())
2375         return;
2376     t1_getline (mp);
2377     if (t1_prefix ("{restore}"))
2378         t1_putline (mp);
2379 }
2380
2381 @ @<Types...@>=
2382 typedef struct {
2383     char *ff_name;              /* base name of font file */
2384     char *ff_path;              /* full path to font file */
2385 } ff_entry;
2386
2387 @ @c
2388 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2389     ff_entry *ff;
2390     ff = check_ff_exist (mp, fm_cur);
2391     if (ff->ff_path != NULL) {
2392         mp->ps->t1_file = mp_open_file(mp,ff->ff_path, "rb", mp_filetype_font);
2393     } else {
2394         mp_warn (mp, "cannot open Type 1 font file for reading");
2395         return false;
2396     }
2397     t1_init_params (mp,(char *)open_name_prefix,fm_cur->ff_name);
2398     mp->ps->fontfile_found = true;
2399     return true;
2400 }
2401
2402 static void  t1_scan_only (MP mp, int tex_font, fm_entry *fm_cur) {
2403     do {
2404         t1_getline (mp);
2405         t1_scan_param (mp,tex_font, fm_cur);
2406     }
2407     while (mp->ps->t1_in_eexec == 0);
2408     t1_start_eexec (mp,fm_cur);
2409     do {
2410         t1_getline (mp);
2411         t1_scan_param (mp,tex_font, fm_cur);
2412     }
2413     while (!(t1_charstrings () || t1_subrs ()));
2414 }
2415
2416 static void  t1_include (MP mp, int tex_font, fm_entry *fm_cur) {
2417     do {
2418         t1_getline (mp);
2419         t1_scan_param (mp,tex_font, fm_cur);
2420         t1_putline (mp);
2421     }
2422     while (mp->ps->t1_in_eexec == 0);
2423     t1_start_eexec (mp,fm_cur);
2424     do {
2425         t1_getline (mp);
2426         t1_scan_param (mp,tex_font, fm_cur);
2427         t1_putline (mp);
2428     }
2429     while (!(t1_charstrings () || t1_subrs ()));
2430     mp->ps->t1_cs = true;
2431     do {
2432         t1_getline (mp);
2433         t1_putline (mp);
2434     }
2435     while (!t1_end_eexec ());
2436     t1_stop_eexec (mp);
2437     if (fixedcontent) {         /* copy 512 zeros (not needed for PDF) */
2438         do {
2439             t1_getline (mp);
2440             t1_putline (mp);
2441         }
2442         while (!t1_cleartomark ());
2443         t1_check_end (mp);        /* write "{restore}if" if found */
2444     }
2445 }
2446
2447 @
2448 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2449         char s[128];
2450         snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2451         mp_fatal_error(mp,s);
2452   }
2453
2454 @c
2455 static const char **check_cs_token_pair (MP mp) {
2456     const char **p = (const char **) cs_token_pairs_list;
2457     for (; p[0] != NULL; ++p)
2458         if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2459             return p;
2460     return NULL;
2461 }
2462
2463 static void cs_store (MP mp, boolean is_subr) {
2464     char *p;
2465     cs_entry *ptr;
2466     int subr;
2467     for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2468          *mp->ps->t1_buf_ptr++ = *p++);
2469     *mp->ps->t1_buf_ptr = 0;
2470     if (is_subr) {
2471         subr = t1_scan_num (mp, p + 1, 0);
2472         check_subr (subr);
2473         ptr = mp->ps->subr_tab + subr;
2474     } else {
2475         ptr = mp->ps->cs_ptr++;
2476         if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2477           char s[128];
2478           snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2479           mp_fatal_error(mp,s);
2480         }
2481         if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0)     /* skip the slash */
2482             ptr->glyph_name = (char *) notdef;
2483         else
2484             ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2485     }
2486     /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2487     memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2488             (unsigned) (mp->ps->t1_cslen + 4));
2489     /* copy the end of cs data to |mp->ps->t1_buf_array| */
2490     for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2491          mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2492     *mp->ps->t1_buf_ptr++ = 10;
2493     if (is_subr && mp->ps->cs_token_pair == NULL)
2494         mp->ps->cs_token_pair = check_cs_token_pair (mp);
2495     ptr->len = mp->ps->t1_buf_ptr - mp->ps->t1_buf_array;
2496     ptr->cslen = mp->ps->t1_cslen;
2497     ptr->data = mp_xmalloc (mp,ptr->len , sizeof (byte));
2498     memcpy (ptr->data, mp->ps->t1_buf_array, ptr->len);
2499     ptr->valid = true;
2500 }
2501
2502 #define store_subr(mp)    cs_store(mp,true)
2503 #define store_cs(mp)      cs_store(mp,false)
2504
2505 #define CC_STACK_SIZE       24
2506
2507 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2508 static cc_entry cc_tab[CS_MAX];
2509 static boolean is_cc_init = false;
2510
2511
2512 #define cc_pop(N)                       \
2513     if (stack_ptr - cc_stack < (N))     \
2514         stack_error(N);                 \
2515     stack_ptr -= N
2516
2517 #define stack_error(N) {                \
2518     char s[256];                        \
2519     snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2520                  (int) N, (int)(stack_ptr - cc_stack));                  \
2521     mp_warn(mp,s);                    \
2522     goto cs_error;                    \
2523 }
2524
2525
2526 #define cc_get(N)   ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2527
2528 #define cc_push(V)  *stack_ptr++ = V
2529 #define cc_clear()  stack_ptr = cc_stack
2530
2531 #define set_cc(N, B, A, C) \
2532     cc_tab[N].nargs = A;   \
2533     cc_tab[N].bottom = B;  \
2534     cc_tab[N].clear = C;   \
2535     cc_tab[N].valid = true
2536
2537 static void cc_init (void) {
2538     int i;
2539     if (is_cc_init)
2540         return;
2541     for (i = 0; i < CS_MAX; i++)
2542         cc_tab[i].valid = false;
2543     set_cc (CS_HSTEM, true, 2, true);
2544     set_cc (CS_VSTEM, true, 2, true);
2545     set_cc (CS_VMOVETO, true, 1, true);
2546     set_cc (CS_RLINETO, true, 2, true);
2547     set_cc (CS_HLINETO, true, 1, true);
2548     set_cc (CS_VLINETO, true, 1, true);
2549     set_cc (CS_RRCURVETO, true, 6, true);
2550     set_cc (CS_CLOSEPATH, false, 0, true);
2551     set_cc (CS_CALLSUBR, false, 1, false);
2552     set_cc (CS_RETURN, false, 0, false);
2553     /*
2554        |set_cc(CS_ESCAPE,          false,  0, false);|
2555      */
2556     set_cc (CS_HSBW, true, 2, true);
2557     set_cc (CS_ENDCHAR, false, 0, true);
2558     set_cc (CS_RMOVETO, true, 2, true);
2559     set_cc (CS_HMOVETO, true, 1, true);
2560     set_cc (CS_VHCURVETO, true, 4, true);
2561     set_cc (CS_HVCURVETO, true, 4, true);
2562     set_cc (CS_DOTSECTION, false, 0, true);
2563     set_cc (CS_VSTEM3, true, 6, true);
2564     set_cc (CS_HSTEM3, true, 6, true);
2565     set_cc (CS_SEAC, true, 5, true);
2566     set_cc (CS_SBW, true, 4, true);
2567     set_cc (CS_DIV, false, 2, false);
2568     set_cc (CS_CALLOTHERSUBR, false, 0, false);
2569     set_cc (CS_POP, false, 0, false);
2570     set_cc (CS_SETCURRENTPOINT, true, 2, true);
2571     is_cc_init = true;
2572 }
2573
2574 @
2575
2576 @d cs_getchar(mp)    cdecrypt(mp,*data++, &cr)
2577
2578 @d mark_subr(mp,n)    cs_mark(mp,0, n)
2579 @d mark_cs(mp,s)      cs_mark(mp,s, 0)
2580 @d SMALL_BUF_SIZE      256
2581
2582 @c
2583 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2584     char buf[SMALL_BUF_SIZE];
2585     char s[300];
2586     va_list args;
2587     va_start (args, fmt);
2588     vsprintf (buf, fmt, args);
2589     va_end (args);
2590     if (cs_name == NULL) {
2591         snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2592     } else {
2593        snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2594     }
2595     mp_warn(mp,s);
2596 }
2597
2598 static void cs_mark (MP mp, const char *cs_name, int subr)
2599 {
2600     byte *data;
2601     int i, b, cs_len;
2602     integer a, a1, a2;
2603     unsigned short cr;
2604     static integer lastargOtherSubr3 = 3;       /* the argument of last call to
2605                                                    OtherSubrs[3] */
2606     cs_entry *ptr;
2607     cc_entry *cc;
2608     if (cs_name == NULL) {
2609         check_subr (subr);
2610         ptr = mp->ps->subr_tab + subr;
2611         if (!ptr->valid)
2612           return;
2613     } else {
2614         if (mp->ps->cs_notdef != NULL &&
2615             (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2616             ptr = mp->ps->cs_notdef;
2617         else {
2618             for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2619                 if (strcmp (ptr->glyph_name, cs_name) == 0)
2620                     break;
2621             if (ptr == mp->ps->cs_ptr) {
2622                 char s[128];
2623                 snprintf (s,128,"glyph `%s' undefined", cs_name);
2624                 mp_warn(mp,s);
2625                 return;
2626             }
2627             if (ptr->glyph_name == notdef)
2628                 mp->ps->cs_notdef = ptr;
2629         }
2630     }
2631     /* only marked CharString entries and invalid entries can be skipped;
2632        valid marked subrs must be parsed to keep the stack in sync */
2633     if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2634         return;
2635     ptr->is_used = true;
2636     cr = 4330;
2637     cs_len = ptr->cslen;
2638     data = ptr->data + 4;
2639     for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2640         cs_getchar (mp);
2641     while (cs_len > 0) {
2642         --cs_len;
2643         b = cs_getchar (mp);
2644         if (b >= 32) {
2645             if (b <= 246)
2646                 a = b - 139;
2647             else if (b <= 250) {
2648                 --cs_len;
2649                 a = ((b - 247) << 8) + 108 + cs_getchar (mp);
2650             } else if (b <= 254) {
2651                 --cs_len;
2652                 a = -((b - 251) << 8) - 108 - cs_getchar (mp);
2653             } else {
2654                 cs_len -= 4;
2655                 a = (cs_getchar (mp) & 0xff) << 24;
2656                 a |= (cs_getchar (mp) & 0xff) << 16;
2657                 a |= (cs_getchar (mp) & 0xff) << 8;
2658                 a |= (cs_getchar (mp) & 0xff) << 0;
2659                 if (sizeof (integer) > 4 && (a & 0x80000000))
2660                     a |= ~0x7FFFFFFF;
2661             }
2662             cc_push (a);
2663         } else {
2664             if (b == CS_ESCAPE) {
2665                 b = cs_getchar (mp) + CS_1BYTE_MAX;
2666                 cs_len--;
2667             }
2668             if (b >= CS_MAX) {
2669                 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2670                          (int) b);
2671                 goto cs_error;
2672             }
2673             cc = cc_tab + b;
2674             if (!cc->valid) {
2675                 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2676                 goto cs_error;
2677             }
2678             if (cc->bottom) {
2679                 if (stack_ptr - cc_stack < cc->nargs)
2680                     cs_warn (mp,cs_name, subr,
2681                              "less arguments on stack (%i) than required (%i)",
2682                              (int) (stack_ptr - cc_stack), (int) cc->nargs);
2683                 else if (stack_ptr - cc_stack > cc->nargs)
2684                     cs_warn (mp,cs_name, subr,
2685                              "more arguments on stack (%i) than required (%i)",
2686                              (int) (stack_ptr - cc_stack), (int) cc->nargs);
2687             }
2688             switch (cc - cc_tab) {
2689             case CS_CALLSUBR:
2690                 a1 = cc_get (-1);
2691                 cc_pop (1);
2692                 mark_subr (mp,a1);
2693                 if (!mp->ps->subr_tab[a1].valid) {
2694                     cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2695                     goto cs_error;
2696                 }
2697                 break;
2698             case CS_DIV:
2699                 cc_pop (2);
2700                 cc_push (0);
2701                 break;
2702             case CS_CALLOTHERSUBR:
2703                 if (cc_get (-1) == 3)
2704                     lastargOtherSubr3 = cc_get (-3);
2705                 a1 = cc_get (-2) + 2;
2706                 cc_pop (a1);
2707                 break;
2708             case CS_POP:
2709                 cc_push (lastargOtherSubr3);
2710                 /* the only case when we care about the value being pushed onto
2711                    stack is when POP follows CALLOTHERSUBR (changing hints by
2712                    OtherSubrs[3])
2713                  */
2714                 break;
2715             case CS_SEAC:
2716                 a1 = cc_get (3);
2717                 a2 = cc_get (4);
2718                 cc_clear ();
2719                 mark_cs (mp,standard_glyph_names[a1]);
2720                 mark_cs (mp,standard_glyph_names[a2]);
2721                 break;
2722             default:
2723                 if (cc->clear)
2724                     cc_clear ();
2725             }
2726         }
2727     }
2728     return;
2729   cs_error:                    /* an error occured during parsing */
2730     cc_clear ();
2731     ptr->valid = false;
2732     ptr->is_used = false;
2733 }
2734
2735 static void t1_subset_ascii_part (MP mp, int tex_font, fm_entry *fm_cur)
2736 {
2737     int i, j;
2738     t1_getline (mp);
2739     while (!t1_prefix ("/Encoding")) {
2740           t1_scan_param (mp,tex_font, fm_cur);
2741         t1_putline (mp);
2742         t1_getline (mp);
2743     }
2744     t1_builtin_enc (mp);
2745     if (is_reencoded (fm_cur))
2746         mp->ps->t1_glyph_names = external_enc ();
2747     else
2748         mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2749         /* 
2750     |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2751             make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2752         update_subset_tag ();
2753     }|
2754     */
2755     if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2756         t1_puts (mp,"/Encoding StandardEncoding def\n");
2757     else {
2758         t1_puts
2759             (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2760         for (i = 0, j = 0; i < 256; i++) {
2761             if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2762                 j++;
2763                 t1_printf (mp,"dup %i /%s put\n", (int) t1_char (i),
2764                            mp->ps->t1_glyph_names[i]);
2765             }
2766         }
2767         /* We didn't mark anything for the Encoding array. */
2768         /* We add "dup 0 /.notdef put" for compatibility   */
2769         /* with Acrobat 5.0.                               */
2770         if (j == 0)
2771             t1_puts (mp,"dup 0 /.notdef put\n");
2772         t1_puts (mp,"readonly def\n");
2773     }
2774     do {
2775         t1_getline (mp);
2776         t1_scan_param (mp,tex_font, fm_cur);
2777         if (!t1_prefix ("/UniqueID"))   /* ignore UniqueID for subsetted fonts */
2778             t1_putline (mp);
2779     }
2780     while (mp->ps->t1_in_eexec == 0);
2781 }
2782
2783 #define t1_subr_flush(mp)  t1_flush_cs(mp,true)
2784 #define t1_cs_flush(mp)    t1_flush_cs(mp,false)
2785
2786 static void cs_init (MP mp) {
2787     mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2788     mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2789     mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2790     mp->ps->cs_token_pair = NULL;
2791     mp->ps->subr_tab = NULL;
2792     mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2793     mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2794 }
2795
2796 static void init_cs_entry ( cs_entry * cs) {
2797     cs->data = NULL;
2798     cs->glyph_name = NULL;
2799     cs->len = 0;
2800     cs->cslen = 0;
2801     cs->is_used = false;
2802     cs->valid = false;
2803 }
2804
2805 static void t1_mark_glyphs (MP mp, int tex_font);
2806
2807 static void t1_read_subrs (MP mp, int tex_font, fm_entry *fm_cur)
2808 {
2809     int i, s;
2810     cs_entry *ptr;
2811     t1_getline (mp);
2812     while (!(t1_charstrings () || t1_subrs ())) {
2813         t1_scan_param (mp,tex_font, fm_cur);
2814         t1_putline (mp);
2815         t1_getline (mp);
2816     }
2817   FOUND:
2818     mp->ps->t1_cs = true;
2819     mp->ps->t1_scan = false;
2820     if (!t1_subrs ())
2821         return;
2822     mp->ps->subr_size_pos = strlen ("/Subrs") + 1;
2823     /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2824     mp->ps->subr_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2825     if (mp->ps->subr_size == 0) {
2826         while (!t1_charstrings ())
2827             t1_getline (mp);
2828         return;
2829     }
2830         /*    |subr_tab = xtalloc (subr_size, cs_entry);| */
2831         mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,mp->ps->subr_size, sizeof (cs_entry));
2832     for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2833         init_cs_entry (ptr);
2834     mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2835     t1_getline (mp);
2836     while (mp->ps->t1_cslen) {
2837         store_subr (mp);
2838         t1_getline (mp);
2839     }
2840     /* mark the first four entries without parsing */
2841     for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2842         mp->ps->subr_tab[i].is_used = true;
2843     /* the end of the Subrs array might have more than one line so we need to
2844        concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2845        the Subrs array followed by the CharStrings dict immediately (synthetic
2846        fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2847        we will treat the font as synthetic and ignore everything until next
2848        Subrs is found
2849      */
2850 #define POST_SUBRS_SCAN     5
2851     s = 0;
2852     *mp->ps->t1_buf_array = 0;
2853     for (i = 0; i < POST_SUBRS_SCAN; i++) {
2854         if (t1_charstrings ())
2855             break;
2856         s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2857         alloc_array (t1_buf, s, T1_BUF_SIZE);
2858         strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2859         t1_getline (mp);
2860     }
2861     mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2862     if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2863                                    suppose synthetic font */
2864         for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2865             if (ptr->valid)
2866                 mp_xfree (ptr->data);
2867         mp_xfree (mp->ps->subr_tab);
2868         mp_xfree (mp->ps->subr_array_start);
2869         mp_xfree (mp->ps->subr_array_end);
2870         cs_init (mp);
2871         mp->ps->t1_cs = false;
2872         mp->ps->t1_synthetic = true;
2873         while (!(t1_charstrings () || t1_subrs ()))
2874             t1_getline (mp);
2875         goto FOUND;
2876     }
2877 }
2878
2879 @ @c
2880 static void t1_flush_cs (MP mp, boolean is_subr)
2881 {
2882     char *p;
2883     byte *r, *return_cs = NULL;
2884     cs_entry *tab, *end_tab, *ptr;
2885     char *start_line, *line_end;
2886     int count, size_pos;
2887     unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2888     if (is_subr) {
2889         start_line = mp->ps->subr_array_start;
2890         line_end =  mp->ps->subr_array_end;
2891         size_pos =  mp->ps->subr_size_pos;
2892         tab =  mp->ps->subr_tab;
2893         count =  mp->ps->subr_max + 1;
2894         end_tab =  mp->ps->subr_tab + count;
2895     } else {
2896         start_line =  mp->ps->cs_dict_start;
2897         line_end =  mp->ps->cs_dict_end;
2898         size_pos =  mp->ps->cs_size_pos;
2899         tab =  mp->ps->cs_tab;
2900         end_tab =  mp->ps->cs_ptr;
2901         count =  mp->ps->cs_count;
2902     }
2903     mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2904     for (p = start_line; p - start_line < size_pos;)
2905         *mp->ps->t1_line_ptr++ = *p++;
2906     while (isdigit (*p))
2907         p++;
2908     sprintf (mp->ps->t1_line_ptr, "%u", count);
2909     strcat (mp->ps->t1_line_ptr, p);
2910     mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2911     t1_putline (mp);
2912
2913     /* create |return_cs| to replace unsused subr's */
2914     if (is_subr) {
2915         cr = 4330;
2916         cs_len = 0;
2917         return_cs = mp_xmalloc (mp, (mp->ps->t1_lenIV + 1) , sizeof(byte));
2918         if ( mp->ps->t1_lenIV > 0) {
2919             for (cs_len = 0, r = return_cs; cs_len <  mp->ps->t1_lenIV; cs_len++, r++)
2920                 *r = cencrypt (mp,0x00, &cr);
2921             *r = cencrypt (mp,CS_RETURN, &cr);
2922         } else {
2923             *return_cs = CS_RETURN;
2924         }
2925         cs_len++;
2926     }
2927
2928     for (ptr = tab; ptr < end_tab; ptr++) {
2929         if (ptr->is_used) {
2930             if (is_subr)
2931                 sprintf (mp->ps->t1_line_array, "dup %i %u", (int) (ptr - tab),
2932                          ptr->cslen);
2933             else
2934                 sprintf (mp->ps->t1_line_array, "/%s %u", ptr->glyph_name, ptr->cslen);
2935             p = strend (mp->ps->t1_line_array);
2936             memcpy (p, ptr->data, ptr->len);
2937             mp->ps->t1_line_ptr = p + ptr->len;
2938             t1_putline (mp);
2939         } else {
2940             /* replace unsused subr's by |return_cs| */
2941             if (is_subr) {
2942                 sprintf (mp->ps->t1_line_array, "dup %i %u%s ", (int) (ptr - tab),
2943                          cs_len,  mp->ps->cs_token_pair[0]);
2944                 p = strend (mp->ps->t1_line_array);
2945                 memcpy (p, return_cs, cs_len);
2946                 mp->ps->t1_line_ptr = p + cs_len;
2947                 t1_putline (mp);
2948                 sprintf (mp->ps->t1_line_array, " %s",  mp->ps->cs_token_pair[1]);
2949                 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2950                 t1_putline (mp);
2951             }
2952         }
2953         mp_xfree (ptr->data);
2954         if (ptr->glyph_name != notdef)
2955             mp_xfree (ptr->glyph_name);
2956     }
2957     sprintf (mp->ps->t1_line_array, "%s", line_end);
2958     mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2959     t1_putline (mp);
2960     if (is_subr)
2961         mp_xfree (return_cs);
2962     mp_xfree (tab);
2963     mp_xfree (start_line);
2964     mp_xfree (line_end);
2965 }
2966
2967 static void t1_mark_glyphs (MP mp, int tex_font)
2968 {
2969     int i;
2970     char *charset = extra_charset ();
2971     char *g, *s, *r;
2972     cs_entry *ptr;
2973     if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) {  /* mark everything */
2974         if (mp->ps->cs_tab != NULL)
2975             for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2976                 if (ptr->valid)
2977                     ptr->is_used = true;
2978         if (mp->ps->subr_tab != NULL) {
2979             for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2980                 if (ptr->valid)
2981                     ptr->is_used = true;
2982             mp->ps->subr_max = mp->ps->subr_size - 1;
2983         }
2984         return;
2985     }
2986     mark_cs (mp,notdef);
2987     for (i = 0; i < 256; i++)
2988         if (is_used_char (i)) {
2989             if (mp->ps->t1_glyph_names[i] == notdef) {
2990                 char s[128];
2991                 snprintf(s,128, "character %i is mapped to %s", i, notdef);
2992                 mp_warn(mp,s);
2993             } else
2994                 mark_cs (mp,mp->ps->t1_glyph_names[i]);
2995         }
2996     if (charset == NULL)
2997         goto SET_SUBR_MAX;
2998     g = s = charset + 1;        /* skip the first '/' */
2999     r = strend (g);
3000     while (g < r) {
3001         while (*s != '/' && s < r)
3002             s++;
3003         *s = 0;                 /* terminate g by rewriting '/' to 0 */
3004         mark_cs (mp,g);
3005         g = s + 1;
3006     }
3007   SET_SUBR_MAX:
3008     if (mp->ps->subr_tab != NULL)
3009         for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab; 
3010                  ptr - mp->ps->subr_tab < mp->ps->subr_size; 
3011              ptr++)
3012             if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3013                 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3014 }
3015
3016 static void t1_subset_charstrings (MP mp, int tex_font) 
3017 {
3018     cs_entry *ptr;
3019     mp->ps->cs_size_pos =
3020         strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3021         - mp->ps->t1_line_array + 1;
3022     /* |cs_size_pos| points to the number indicating
3023        dict size after "/CharStrings" */
3024     mp->ps->cs_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3025     mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,mp->ps->cs_size, sizeof(cs_entry));
3026     for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3027         init_cs_entry (ptr);
3028     mp->ps->cs_notdef = NULL;
3029     mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3030     t1_getline (mp);
3031     while (mp->ps->t1_cslen) {
3032         store_cs (mp);
3033         t1_getline (mp);
3034     }
3035     mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3036     t1_mark_glyphs (mp,tex_font);
3037     if (mp->ps->subr_tab != NULL) {
3038         if (mp->ps->cs_token_pair == NULL) 
3039             mp_fatal_error
3040                 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3041         t1_subr_flush (mp);
3042     }
3043     for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3044         if (ptr->is_used)
3045             mp->ps->cs_count++;
3046     t1_cs_flush (mp);
3047 }
3048
3049 static void t1_subset_end (MP mp)
3050 {
3051     if (mp->ps->t1_synthetic) {         /* copy to "dup /FontName get exch definefont pop" */
3052         while (!strstr (mp->ps->t1_line_array, "definefont")) {
3053             t1_getline (mp);
3054             t1_putline (mp);
3055         }
3056         while (!t1_end_eexec ())
3057             t1_getline (mp);      /* ignore the rest */
3058         t1_putline (mp);          /* write "mark currentfile closefile" */
3059     } else
3060         while (!t1_end_eexec ()) {      /* copy to "mark currentfile closefile" */
3061             t1_getline (mp);
3062             t1_putline (mp);
3063         }
3064     t1_stop_eexec (mp);
3065     if (fixedcontent) {         /* copy 512 zeros (not needed for PDF) */
3066         while (!t1_cleartomark ()) {
3067             t1_getline (mp);
3068             t1_putline (mp);
3069         }
3070         if (!mp->ps->t1_synthetic)      /* don't check "{restore}if" for synthetic fonts */
3071             t1_check_end (mp);    /* write "{restore}if" if found */
3072     }
3073 }
3074
3075 static int t1_updatefm (MP mp, int f, fm_entry *fm)
3076 {
3077   char *s, *p;
3078   mp->ps->read_encoding_only = true;
3079   if (!t1_open_fontfile (mp,fm,NULL)) {
3080         return 0;
3081   }
3082   t1_scan_only (mp,f, fm);
3083   s = mp_xstrdup(mp,mp->ps->fontname_buf);
3084   p = s;
3085   while (*p != ' ' && *p != 0) 
3086      p++;
3087   *p=0;
3088   fm->ps_name = s;
3089   t1_close_font_file (mp,"");
3090   return 1;
3091 }
3092
3093
3094 static void  writet1 (MP mp, int tex_font, fm_entry *fm_cur) {
3095         int save_selector = mp->selector;
3096     mp_normalize_selector(mp);
3097     mp->ps->read_encoding_only = false;
3098     if (!is_included (fm_cur)) {        /* scan parameters from font file */
3099       if (!t1_open_fontfile (mp,fm_cur,"{"))
3100             return;
3101             t1_scan_only (mp,tex_font, fm_cur);
3102         t1_close_font_file (mp,"}");
3103         return;
3104     }
3105     if (!is_subsetted (fm_cur)) {       /* include entire font */
3106       if (!t1_open_fontfile (mp,fm_cur,"<<"))
3107             return;
3108           t1_include (mp,tex_font,fm_cur);
3109         t1_close_font_file (mp,">>");
3110         return;
3111     }
3112     /* partial downloading */
3113     if (!t1_open_fontfile (mp,fm_cur,"<"))
3114         return;
3115     t1_subset_ascii_part (mp,tex_font,fm_cur);
3116     t1_start_eexec (mp,fm_cur);
3117     cc_init ();
3118     cs_init (mp);
3119     t1_read_subrs (mp,tex_font, fm_cur);
3120     t1_subset_charstrings (mp,tex_font);
3121     t1_subset_end (mp);
3122     t1_close_font_file (mp,">");
3123     mp->selector = save_selector; 
3124 }
3125
3126 @ @<Declarations@>=
3127 static void t1_free (MP mp);
3128
3129 @ @c
3130 static void  t1_free (MP mp) {
3131   mp_xfree (mp->ps->t1_line_array);
3132   mp_xfree (mp->ps->t1_buf_array);
3133 }
3134
3135
3136 @* \[44d] Embedding fonts.
3137
3138 @ The |tfm_num| is officially of type |font_number|, but that
3139 type does not exist yet at this point in the output order.
3140
3141 @<Types...@>=
3142 typedef struct {
3143     char *tfm_name;             /* TFM file name */
3144     char *ps_name;              /* PostScript name */
3145     integer flags;              /* font flags */
3146     char *ff_name;              /* font file name */
3147     char *subset_tag;           /* pseudoUniqueTag for subsetted font */
3148     enc_entry *encoding;        /* pointer to corresponding encoding */
3149     unsigned int tfm_num;       /* number of the TFM refering this entry */
3150     unsigned short type;        /* font type (T1/TTF/...) */
3151     short slant;                /* SlantFont */
3152     short extend;               /* ExtendFont */
3153     integer ff_objnum;          /* FontFile object number */
3154     integer fn_objnum;          /* FontName/BaseName object number */
3155     integer fd_objnum;          /* FontDescriptor object number */
3156     char *charset;              /* string containing used glyphs */
3157     boolean all_glyphs;         /* embed all glyphs? */
3158     unsigned short links;       /* link flags from |tfm_tree| and |ps_tree| */
3159     short tfm_avail;            /* flags whether a tfm is available */
3160     short pid;                  /* Pid for truetype fonts */
3161     short eid;                  /* Eid for truetype fonts */
3162 } fm_entry;
3163
3164
3165
3166 @<Glob...@>=
3167 #define FONTNAME_BUF_SIZE 128
3168 boolean fontfile_found;
3169 boolean is_otf_font;
3170 char fontname_buf[FONTNAME_BUF_SIZE];
3171
3172
3173 @d F_INCLUDED          0x01
3174 @d F_SUBSETTED         0x02
3175 @d F_TRUETYPE          0x04
3176 @d F_BASEFONT          0x08
3177
3178 @d set_included(fm)    ((fm)->type |= F_INCLUDED)
3179 @d set_subsetted(fm)   ((fm)->type |= F_SUBSETTED)
3180 @d set_truetype(fm)    ((fm)->type |= F_TRUETYPE)
3181 @d set_basefont(fm)    ((fm)->type |= F_BASEFONT)
3182
3183 @d is_included(fm)     ((fm)->type & F_INCLUDED)
3184 @d is_subsetted(fm)    ((fm)->type & F_SUBSETTED)
3185 @d is_truetype(fm)     ((fm)->type & F_TRUETYPE)
3186 @d is_basefont(fm)     ((fm)->type & F_BASEFONT)
3187 @d is_reencoded(fm)    ((fm)->encoding != NULL)
3188 @d is_fontfile(fm)     (fm_fontfile(fm) != NULL)
3189 @d is_t1fontfile(fm)   (is_fontfile(fm) && !is_truetype(fm))
3190
3191 @d fm_slant(fm)        (fm)->slant
3192 @d fm_extend(fm)       (fm)->extend
3193 @d fm_fontfile(fm)     (fm)->ff_name
3194
3195 @<Declarations@>=
3196 static boolean mp_font_is_reencoded (MP mp, int f);
3197 static boolean mp_font_is_included (MP mp, int f);
3198 static boolean mp_font_is_subsetted (MP mp, int f);
3199
3200 @ @c
3201 static boolean mp_font_is_reencoded (MP mp, int f) {
3202   fm_entry *fm;
3203   if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) { 
3204     if (fm != NULL 
3205         && (fm->ps_name != NULL)
3206         && is_reencoded (fm))
3207       return 1;
3208   }
3209   return 0;
3210 }
3211 static boolean mp_font_is_included (MP mp, int f) {
3212   fm_entry *fm;
3213   if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) { 
3214     if (fm != NULL 
3215         && (fm->ps_name != NULL && fm->ff_name != NULL) 
3216         && is_included (fm))
3217       return 1;
3218   }
3219   return 0;
3220 }
3221 static boolean mp_font_is_subsetted (MP mp, int f) {
3222   fm_entry *fm;
3223   if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) { 
3224     if (fm != NULL 
3225           && (fm->ps_name != NULL && fm->ff_name != NULL) 
3226           && is_included (fm) && is_subsetted (fm))
3227       return 1;
3228   }
3229   return 0;
3230 }
3231
3232 @ @<Exported function headers@>=
3233 char * mp_fm_encoding_name (MP mp, int f);
3234 char * mp_fm_font_name (MP mp, int f);
3235
3236 @ @<Declarations@>=
3237 static char * mp_fm_font_subset_name (MP mp, int f);
3238
3239
3240 @c char * mp_fm_encoding_name (MP mp, int f) {
3241   enc_entry *e;
3242   fm_entry *fm;
3243   if (mp_has_fm_entry (mp, f, &fm)) { 
3244     if (fm != NULL && (fm->ps_name != NULL)) {
3245       if (is_reencoded (fm)) {
3246         e = fm->encoding;
3247         if (e->enc_name!=NULL)
3248           return mp_xstrdup(mp,e->enc_name);
3249       } else {
3250         return NULL;
3251       }
3252     }
3253   }
3254   print_err ("fontmap encoding problems for font ");
3255   mp_print(mp,mp->font_name[f]);
3256   mp_error(mp); 
3257   return NULL;
3258 }
3259 char * mp_fm_font_name (MP mp, int f) {
3260   fm_entry *fm;
3261   if (mp_has_fm_entry (mp, f,&fm)) { 
3262     if (fm != NULL && (fm->ps_name != NULL)) {
3263       if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3264         /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3265         if (t1_updatefm(mp,f,fm)) {
3266           mp->font_ps_name_fixed[f] = true;
3267         } else {
3268           print_err ("font loading problems for font ");
3269           mp_print(mp,mp->font_name[f]);
3270           mp_error(mp);
3271         }
3272       }
3273       return mp_xstrdup(mp,fm->ps_name);
3274     }
3275   }
3276   print_err ("fontmap name problems for font ");
3277   mp_print(mp,mp->font_name[f]);
3278   mp_error(mp); 
3279   return NULL;
3280 }
3281
3282 static char * mp_fm_font_subset_name (MP mp, int f) {
3283   fm_entry *fm;
3284   if (mp_has_fm_entry (mp, f, &fm)) { 
3285     if (fm != NULL && (fm->ps_name != NULL)) {
3286       if (is_subsetted(fm)) {
3287             char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3288         snprintf(s,strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3289             return s;
3290       } else {
3291         return mp_xstrdup(mp,fm->ps_name);
3292       }
3293     }
3294   }
3295   print_err ("fontmap name problems for font ");
3296   mp_print(mp,mp->font_name[f]);
3297   mp_error(mp); 
3298   return NULL;
3299 }
3300
3301 @ @<Declarations@>=
3302 static integer mp_fm_font_slant (MP mp, int f);
3303 static integer mp_fm_font_extend (MP mp, int f);
3304
3305
3306 @c static integer mp_fm_font_slant (MP mp, int f) {
3307   fm_entry *fm;
3308   if (mp_has_fm_entry (mp, f, &fm)) { 
3309     if (fm != NULL && (fm->ps_name != NULL)) {
3310       return fm->slant;
3311     }
3312   }
3313   return 0;
3314 }
3315 static integer mp_fm_font_extend (MP mp, int f) {
3316   fm_entry *fm;
3317   if (mp_has_fm_entry (mp, f, &fm)) { 
3318     if (fm != NULL && (fm->ps_name != NULL)) {
3319       return fm->extend;
3320     }
3321   }
3322   return 0;
3323 }
3324
3325 @ @<Declarations@>=
3326 static boolean mp_do_ps_font (MP mp, font_number f);
3327
3328 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3329   fm_entry *fm_cur;
3330   (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3331   if (fm_cur == NULL)
3332     return 1;
3333   if (is_truetype(fm_cur) ||
3334          (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3335     return 0;
3336   }
3337   if (is_included(fm_cur)) {
3338     mp_print_nl(mp,"%%BeginResource: font ");
3339     if (is_subsetted(fm_cur)) {
3340       mp_print(mp, fm_cur->subset_tag);
3341       mp_print_char(mp,'-');
3342     }
3343     mp_print(mp, fm_cur->ps_name);
3344     mp_print_ln(mp);
3345     writet1 (mp,f,fm_cur);
3346     mp_print_nl(mp,"%%EndResource");
3347     mp_print_ln(mp);
3348   }
3349   return 1;
3350 }
3351
3352 @ Included subset fonts do not need and encoding vector, make
3353 sure we skip that case.
3354
3355 @<Declarations@>=
3356 static void mp_list_used_resources (MP mp);
3357
3358 @ @c static void mp_list_used_resources (MP mp) {
3359   font_number f; /* fonts used in a text node or as loop counters */
3360   int ff;  /* a loop counter */
3361   font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3362   boolean firstitem;
3363   int prologues = (mp->internal[mp_prologues]>>16);
3364   int procset = (mp->internal[mp_procset]>>16);
3365
3366   if ( procset>0 )
3367     mp_print_nl(mp, "%%DocumentResources: procset mpost");
3368   else
3369     mp_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3370   ldf=null_font;
3371   firstitem=true;
3372   for (f=null_font+1;f<=mp->last_fnum;f++) {
3373     if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3374           for (ff=ldf;ff>=null_font;ff--) {
3375         if ( mp_has_font_size(mp,ff) )
3376           if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3377             goto FOUND;
3378       }
3379       if ( mp_font_is_subsetted(mp,f) )
3380         goto FOUND;
3381       if ( mp->ps_offset+1+strlen(mp->font_enc_name[f])>
3382            (unsigned)mp->max_print_line )
3383         mp_print_nl(mp, "%%+ encoding");
3384       if ( firstitem ) {
3385         firstitem=false;
3386         mp_print_nl(mp, "%%+ encoding");
3387       }
3388       mp_print_char(mp, ' ');
3389       mp_print(mp, mp->font_enc_name[f]);
3390       ldf=f;
3391     }
3392   FOUND:
3393     ;
3394   }
3395   ldf=null_font;
3396   firstitem=true;
3397   for (f=null_font+1;f<=mp->last_fnum;f++) {
3398     if ( mp_has_font_size(mp,f) ) {
3399       for (ff=ldf;ff>=null_font;ff--) {
3400         if ( mp_has_font_size(mp,ff) )
3401           if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3402             goto FOUND2;
3403       }
3404       if ( mp->ps_offset+1+strlen(mp->font_ps_name[f])>
3405                (unsigned)mp->max_print_line )
3406         mp_print_nl(mp, "%%+ font");
3407       if ( firstitem ) {
3408         firstitem=false;
3409         mp_print_nl(mp, "%%+ font");
3410       }
3411       mp_print_char(mp, ' ');
3412           if ( (prologues==3)&&
3413            (mp_font_is_subsetted(mp,f)) )
3414         mp_print(mp, mp_fm_font_subset_name(mp,f));
3415       else
3416         mp_print(mp, mp->font_ps_name[f]);
3417       ldf=f;
3418     }
3419   FOUND2:
3420     ;
3421   }
3422   mp_print_ln(mp);
3423
3424
3425 @ @<Declarations@>=
3426 static void mp_list_supplied_resources (MP mp);
3427
3428 @ @c static void mp_list_supplied_resources (MP mp) {
3429   font_number f; /* fonts used in a text node or as loop counters */
3430   int ff; /* a loop counter */
3431   font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3432   boolean firstitem;
3433   int prologues = (mp->internal[mp_prologues]>>16);
3434   int procset = (mp->internal[mp_procset]>>16);
3435   if ( procset>0 )
3436     mp_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3437   else
3438     mp_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3439   ldf=null_font;
3440   firstitem=true;
3441   for (f=null_font+1;f<=mp->last_fnum;f++) {
3442     if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) )  {
3443        for (ff=ldf;ff>= null_font;ff++) {
3444          if ( mp_has_font_size(mp,ff) )
3445            if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3446              goto FOUND;
3447         }
3448       if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3449         goto FOUND;
3450       if ( mp->ps_offset+1+strlen(mp->font_enc_name[f])>(unsigned)mp->max_print_line )
3451         mp_print_nl(mp, "%%+ encoding");
3452       if ( firstitem ) {
3453         firstitem=false;
3454         mp_print_nl(mp, "%%+ encoding");
3455       }
3456       mp_print_char(mp, ' ');
3457       mp_print(mp, mp->font_enc_name[f]);
3458       ldf=f;
3459     }
3460   FOUND:
3461     ;
3462   }
3463   ldf=null_font;
3464   firstitem=true;
3465   if (prologues==3) {
3466     for (f=null_font+1;f<=mp->last_fnum;f++) {
3467       if ( mp_has_font_size(mp,f) ) {
3468         for (ff=ldf;ff>= null_font;ff--) {
3469           if ( mp_has_font_size(mp,ff) )
3470             if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3471                goto FOUND2;
3472         }
3473         if ( ! mp_font_is_included(mp,f) )
3474           goto FOUND2;
3475         if ( mp->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3476           mp_print_nl(mp, "%%+ font");
3477         if ( firstitem ) {
3478           firstitem=false;
3479           mp_print_nl(mp, "%%+ font");
3480         }
3481         mp_print_char(mp, ' ');
3482             if ( mp_font_is_subsetted(mp,f) ) 
3483           mp_print(mp, mp_fm_font_subset_name(mp,f));
3484         else
3485           mp_print(mp, mp->font_ps_name[f]);
3486         ldf=f;
3487       }
3488     FOUND2:
3489       ;
3490     }
3491     mp_print_ln(mp);
3492   }
3493 }
3494
3495 @ @<Declarations...@>=
3496 static void mp_list_needed_resources (MP mp);
3497
3498 @ @c static void mp_list_needed_resources (MP mp) {
3499   font_number f; /* fonts used in a text node or as loop counters */
3500   int ff; /* a loop counter */
3501   font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3502   boolean firstitem;
3503   int prologues = (mp->internal[mp_prologues]>>16);
3504   ldf=null_font;
3505   firstitem=true;
3506   for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3507     if ( mp_has_font_size(mp,f)) {
3508       for (ff=ldf;ff>=null_font;ff--) {
3509         if ( mp_has_font_size(mp,ff) )
3510           if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3511              goto FOUND;
3512       };
3513       if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3514         goto FOUND;
3515       if ( mp->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3516         mp_print_nl(mp, "%%+ font");
3517       if ( firstitem ) {
3518         firstitem=false;
3519         mp_print_nl(mp, "%%DocumentNeededResources: font");
3520       }
3521       mp_print_char(mp, ' ');
3522       mp_print(mp, mp->font_ps_name[f]);
3523       ldf=f;
3524     }
3525   FOUND:
3526     ;
3527   }
3528   if ( ! firstitem ) {
3529     mp_print_ln(mp);
3530     ldf=null_font;
3531     firstitem=true;
3532     for (f=null_font+1;f<= mp->last_fnum;f++) {
3533       if ( mp_has_font_size(mp,f) ) {
3534         for (ff=ldf;ff>=null_font;ff-- ) {
3535           if ( mp_has_font_size(mp,ff) )
3536             if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3537               goto FOUND2;
3538         }
3539         if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3540           goto FOUND2;
3541         mp_print(mp, "%%IncludeResource: font ");
3542         mp_print(mp, mp->font_ps_name[f]);
3543         mp_print_ln(mp);
3544         ldf=f;
3545       }
3546     FOUND2:
3547       ;
3548     }
3549   }
3550 }
3551
3552 @ @<Declarations@>=
3553 static void mp_write_font_definition (MP mp, font_number f);
3554
3555
3556
3557 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3558     ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3559
3560 @c static void mp_write_font_definition(MP mp, font_number f) {
3561   int prologues = (mp->internal[mp_prologues]>>16);
3562   if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3563        (mp_fm_font_extend(mp,f)!=0)||
3564        (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3565        (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3566     if ( (mp_font_is_subsetted(mp,f))&&
3567          (mp_font_is_included(mp,f))&&(prologues==3))
3568       mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3569     else 
3570       mp_ps_name_out(mp, mp->font_ps_name[f],true);
3571     mp_ps_print(mp, " fcp");
3572     mp_print_ln(mp);
3573     if ( applied_reencoding(f) ) {
3574       mp_ps_print(mp, "/Encoding ");
3575       mp_ps_print(mp, mp->font_enc_name[f]);
3576       mp_ps_print(mp, " def ");
3577     };
3578     if ( mp_fm_font_slant(mp,f)!=0 ) {
3579       mp_print_int(mp, mp_fm_font_slant(mp,f));
3580       mp_ps_print(mp, " SlantFont ");
3581     };
3582     if ( mp_fm_font_extend(mp,f)!=0 ) {
3583       mp_print_int(mp, mp_fm_font_extend(mp,f));
3584       mp_ps_print(mp, " ExtendFont ");
3585     };
3586     if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3587       mp_ps_print(mp, " 890 ScaleFont ");
3588       mp_ps_print(mp, " 277 SlantFont ");
3589     };
3590     if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3591       mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3592       mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3593       mp_ps_print(mp, "/space[0 -278]def ");
3594       mp_ps_print(mp, "/a12[-904 -939]def ");
3595       mp_ps_print(mp, "end def ");
3596     };  
3597     mp_ps_print(mp, "currentdict end");
3598     mp_print_ln(mp);
3599     mp_ps_print_defined_name(mp,f);
3600     mp_ps_print(mp, " exch definefont pop");
3601     mp_print_ln(mp);
3602   }
3603 }
3604
3605 @ @<Declarations@>=
3606 static void mp_ps_print_defined_name (MP mp, font_number f);
3607
3608
3609 @c static void mp_ps_print_defined_name(MP mp, font_number A) {
3610   int prologues = (mp->internal[mp_prologues]>>16);
3611   mp_ps_print(mp, " /");
3612   if ((mp_font_is_subsetted(mp,(A)))&&
3613       (mp_font_is_included(mp,(A)))&&(prologues==3))
3614     mp_print(mp, mp_fm_font_subset_name(mp,(A)));
3615   else 
3616     mp_print(mp, mp->font_ps_name[(A)]);
3617   if ( mp_xstrcmp(mp->font_name[(A)],"psyrgo")==0 )
3618     mp_ps_print(mp, "-Slanted");
3619   if ( mp_xstrcmp(mp->font_name[(A)],"zpzdr-reversed")==0 ) 
3620     mp_ps_print(mp, "-Reverse");
3621   if ( applied_reencoding((A)) ) { 
3622     mp_ps_print(mp, "-");
3623     mp_ps_print(mp, mp->font_enc_name[(A)]); 
3624   }
3625   if ( mp_fm_font_slant(mp,(A))!=0 ) {
3626     mp_ps_print(mp, "-Slant_"); mp_print_int(mp, mp_fm_font_slant(mp,(A))) ;
3627   }
3628   if ( mp_fm_font_extend(mp,(A))!=0 ) {
3629     mp_ps_print(mp, "-Extend_"); mp_print_int(mp, mp_fm_font_extend(mp,(A))); 
3630   }
3631 }
3632
3633 @ @<Include encodings and fonts for edge structure~|h|@>=
3634 mp_font_encodings(mp,mp->last_fnum,prologues==2);
3635 @<Embed fonts that are available@>
3636
3637 @ @<Embed fonts that are available@>=
3638
3639 next_size=0;
3640 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3641 do {  
3642   done_fonts=true;
3643   for (f=null_font+1;f<=mp->last_fnum;f++) {
3644     if ( cur_fsize[f]!=null ) {
3645       if (prologues==3 ) {
3646         if ( ! mp_do_ps_font(mp,f) ) {
3647               if ( mp_has_fm_entry(mp,f, NULL) ) {
3648             print_err("Font embedding failed");
3649             mp_error(mp);
3650           }
3651         }
3652       }
3653       cur_fsize[f]=link(cur_fsize[f]);
3654       if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3655     }
3656   }
3657   if ( ! done_fonts )
3658     @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3659       that size index@>;
3660 } while (! done_fonts);
3661 }
3662
3663 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3664
3665   next_size++;
3666   mp_apply_mark_string_chars(mp, h, next_size);
3667 }
3668
3669 @ We also need to keep track of which characters are used in text nodes
3670 in the edge structure that is being shipped out.  This is done by procedures
3671 that use the left-over |b3| field in the |char_info| words; i.e.,
3672 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3673
3674 @<Types...@>=
3675 enum mp_char_mark_state {mp_unused=0, mp_used};
3676
3677 @ @<Exported...@>=
3678 void mp_mark_string_chars (MP mp,font_number f, str_number s) ;
3679
3680 @ @c
3681 void mp_mark_string_chars (MP mp,font_number f, str_number s) {
3682   integer b; /* |char_base[f]| */
3683   ASCII_code bc,ec; /* only characters between these bounds are marked */
3684   pool_pointer k; /* an index into string |s| */
3685   b=mp->char_base[f];
3686   bc=mp->font_bc[f];
3687   ec=mp->font_ec[f];
3688   k=mp->str_start[mp->next_str[s]]; /* str_stop */
3689   while ( k>mp->str_start[s] ){ 
3690     decr(k);
3691     if ( (mp->str_pool[k]>=bc)&&(mp->str_pool[k]<=ec) )
3692       mp->font_info[b+mp->str_pool[k]].qqqq.b3=mp_used;
3693   }
3694 }
3695
3696
3697 @ @<Exported ...@>=
3698 void mp_unmark_font (MP mp,font_number f) ;
3699
3700 @ @c
3701 void mp_unmark_font (MP mp,font_number f) {
3702   int k; /* an index into |font_info| */
3703   for (k= mp->char_base[f]+mp->font_bc[f];
3704        k<=mp->char_base[f]+mp->font_ec[f];
3705        k++)
3706     mp->font_info[k].qqqq.b3=mp_unused;
3707 }
3708
3709
3710 @ @<Exported...@>=
3711 void mp_print_improved_prologue (MP mp, pointer h) ;
3712
3713
3714 @
3715 @c
3716 void mp_print_improved_prologue (MP mp, pointer h) {
3717   quarterword next_size; /* the size index for fonts being listed */
3718   pointer *cur_fsize; /* current positions in |font_sizes| */
3719   boolean done_fonts; /* have we finished listing the fonts in the header? */
3720   font_number f; /* a font number for loops */
3721    
3722   int prologues = (mp->internal[mp_prologues]>>16);
3723   int procset = (mp->internal[mp_procset]>>16);
3724   int groffmode = (mp->internal[mp_gtroffmode]>>16);
3725
3726   cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3727
3728   mp_list_used_resources(mp);
3729   mp_list_supplied_resources(mp);
3730   mp_list_needed_resources(mp);
3731   mp_print_nl(mp, "%%EndComments");
3732   mp_print_nl(mp, "%%BeginProlog");
3733   if ( procset>0 )
3734     mp_print_nl(mp, "%%BeginResource: procset mpost");
3735   else
3736     mp_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3737   mp_print_nl(mp, "/bd{bind def}bind def"
3738                   "/fshow {exch findfont exch scalefont setfont show}bd");
3739   if ( procset>0 ) @<Print the procset@>;
3740   mp_print_nl(mp, "/fcp{findfont dup length dict begin"
3741                   "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3742   mp_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3743                    "/fmd{/FontMatrix exch def}bd");
3744   mp_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3745                   "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3746   if ( groffmode>0 ) {
3747     mp_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3748                         " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3749     };
3750   mp_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3751                       " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3752   mp_print_nl(mp, "%%EndResource");
3753   @<Include encodings and fonts  for edge structure~|h|@>;
3754   mp_print_nl(mp, "%%EndProlog");
3755   mp_print_nl(mp, "%%BeginSetup");
3756   mp_print_ln(mp);
3757   for (f=null_font+1;f<=mp->last_fnum;f++) {
3758     if ( mp_has_font_size(mp,f) ) {
3759       if ( mp_has_fm_entry(mp,f,NULL) ) {
3760         mp_write_font_definition(mp,f);
3761         mp_ps_name_out(mp, mp->font_name[f],true);
3762         mp_ps_print_defined_name(mp,f);
3763         mp_ps_print(mp, " def");
3764       } else {
3765         char s[256];
3766         snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3767         mp_warn(mp,s);
3768         mp_ps_name_out(mp, mp->font_name[f],true);
3769         mp_ps_name_out(mp, mp->font_name[f],true);
3770         mp_ps_print(mp, " def");
3771       }
3772       mp_print_ln(mp);
3773     }
3774   }
3775   mp_print_nl(mp, "%%EndSetup");
3776   mp_print_nl(mp, "%%Page: 1 1");
3777   mp_print_ln(mp);
3778   mp_xfree(cur_fsize);
3779 }
3780
3781 @ @<Declarations@>=
3782 static font_number mp_print_font_comments (MP mp , pointer h);
3783
3784
3785
3786 @c 
3787 static font_number mp_print_font_comments (MP mp , pointer h) {
3788   quarterword next_size; /* the size index for fonts being listed */
3789   pointer *cur_fsize; /* current positions in |font_sizes| */
3790   int ff; /* a loop counter */
3791   boolean done_fonts; /* have we finished listing the fonts in the header? */
3792   font_number f; /* a font number for loops */
3793   scaled ds; /* design size and scale factor for a text node */
3794   font_number ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3795   int prologues = (mp->internal[mp_prologues]>>16);
3796   cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3797   if ( prologues>0 ) {
3798     @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3799       |font_sizes| and eliminate duplicates@>;
3800   } else { 
3801     next_size=0;
3802     @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3803     do {  done_fonts=true;
3804       for (f=null_font+1;f<=mp->last_fnum;f++) {
3805         if ( cur_fsize[f]!=null ) {
3806           @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3807         }
3808         if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false;  };
3809       }
3810       if ( ! done_fonts ) {
3811         @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3812           that size index@>;
3813       }
3814     } while (! done_fonts);
3815   }
3816   mp_xfree(cur_fsize);
3817   return ldf;
3818 }
3819
3820 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3821 for (f=null_font+1;f<= mp->last_fnum;f++)
3822   cur_fsize[f]=mp->font_sizes[f]
3823
3824 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3825 so we carefully remove duplicates.  There is no harm in using a slow, brute-force
3826 search.
3827
3828 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3829
3830   ldf=null_font;
3831   for (f=null_font+1;f<= mp->last_fnum;f++) {
3832     if ( mp->font_sizes[f]!=null ) {
3833       if ( ldf==null_font ) 
3834         mp_print_nl(mp, "%%DocumentFonts:");
3835       for (ff=ldf;ff>=null_font;ff--) {
3836         if ( mp->font_sizes[ff]!=null )
3837           if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3838             goto FOUND;
3839       }
3840       if ( mp->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3841         mp_print_nl(mp, "%%+");
3842       mp_print_char(mp, ' ');
3843       mp_print(mp, mp->font_ps_name[f]);
3844       ldf=f;
3845     FOUND:
3846       ;
3847     }
3848   }
3849 }
3850
3851 @ @c
3852 static void mp_hex_digit_out (MP mp,small_number d) { 
3853   if ( d<10 ) mp_print_char(mp, d+'0');
3854   else mp_print_char(mp, d+'a'-10);
3855 }
3856
3857 @ We output the marks as a hexadecimal bit string starting at |c| or
3858 |font_bc[f]|, whichever is greater.  If the output has to be truncated
3859 to avoid exceeding |emergency_line_length| the return value says where to
3860 start scanning next time.
3861
3862 @<Declarations@>=
3863 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3864
3865
3866 @d emergency_line_length 255
3867   /* \ps\ output lines can be this long in unusual circumstances */
3868
3869 @c
3870 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3871   eight_bits bc,ec; /* only encode characters between these bounds */
3872   integer lim; /* the maximum number of marks to encode before truncating */
3873   int p; /* |font_info| index for the current character */
3874   int d,b; /* used to construct a hexadecimal digit */
3875   lim=4*(emergency_line_length-mp->ps_offset-4);
3876   bc=mp->font_bc[f];
3877   ec=mp->font_ec[f];
3878   if ( c>bc ) bc=c;
3879   @<Restrict the range |bc..ec| so that it contains no unused characters
3880     at either end and has length at most |lim|@>;
3881   @<Print the initial label indicating that the bitmap starts at |bc|@>;
3882   @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3883   while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3884     p++; ec++;
3885   }
3886   return (ec+1);
3887 }
3888
3889 @ We could save time by setting the return value before the loop that
3890 decrements |ec|, but there is no point in being so tricky.
3891
3892 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3893 p=mp->char_base[f]+bc;
3894 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3895   p++; bc++;
3896 }
3897 if ( ec>=bc+lim ) ec=bc+lim-1;
3898 p=mp->char_base[f]+ec;
3899 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) { 
3900   p--; ec--;
3901 }
3902
3903 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3904 mp_print_char(mp, ' ');
3905 mp_hex_digit_out(mp, bc / 16);
3906 mp_hex_digit_out(mp, bc % 16);
3907 mp_print_char(mp, ':')
3908
3909
3910
3911 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3912 b=8; d=0;
3913 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3914   if ( b==0 ) {
3915     mp_hex_digit_out(mp, d);
3916     d=0; b=8;
3917   }
3918   if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
3919   b=b>>1;
3920 }
3921 mp_hex_digit_out(mp, d)
3922
3923
3924 @ Here is a simple function that determines whether there are any marked
3925 characters in font~|f| with character code at least~|c|.
3926
3927 @<Declarations@>=
3928 static boolean mp_check_ps_marks (MP mp,font_number f, integer  c) ;
3929
3930 @ @c
3931 static boolean mp_check_ps_marks (MP mp,font_number f, integer  c) {
3932   int p; /* |font_info| index for the current character */
3933   for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
3934     if ( mp->font_info[p].qqqq.b3==mp_used ) 
3935        return true;
3936   }
3937   return false;
3938 }
3939
3940
3941 @ If the file name is so long that it can't be printed without exceeding
3942 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
3943 line.  We might have to repeat line in order to get the character usage
3944 information to fit within |emergency_line_length|.
3945
3946 TODO: these two defines are also defined in mp.w!
3947
3948 @d link(A)   mp->mem[(A)].hh.rh /* the |link| field of a memory word */
3949 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
3950
3951 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
3952 { integer t=0;
3953   while ( mp_check_ps_marks(mp, f,t) ) {
3954     mp_print_nl(mp, "%*Font: ");
3955     if ( mp->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
3956       break;
3957     mp_print(mp, mp->font_name[f]);
3958     mp_print_char(mp, ' ');
3959     ds=(mp->font_dsize[f] + 8) / 16;
3960     mp_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
3961     if ( mp->ps_offset+12>emergency_line_length ) break;
3962     mp_print_char(mp, ' ');
3963     mp_print_scaled(mp, ds);
3964     if ( mp->ps_offset+5>emergency_line_length ) break;
3965     t=mp_ps_marks_out(mp, f,t);
3966   }
3967   cur_fsize[f]=link(cur_fsize[f]);
3968 }
3969
3970 @ @<Print the procset@>=
3971 {
3972   mp_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
3973   mp_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
3974   mp_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
3975                   "/p{closepath}bd/n{newpath}bd");
3976   mp_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
3977                   "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
3978   mp_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
3979                   "/Q{grestore}bd/s{scale}bd/t{concat}bd");
3980   mp_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
3981 }
3982
3983
3984 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
3985 arguments use |font_name| instead of |font_ps_name|.  Downloaded bitmap fonts
3986 might not have reasonable |font_ps_name| entries, but we just charge ahead
3987 anyway.  The user should not make \&{prologues} positive if this will cause
3988 trouble.
3989 @:prologues_}{\&{prologues} primitive@>
3990
3991 @<Exported...@>=
3992 void mp_print_prologue (MP mp, pointer h);
3993
3994 @ @c 
3995 void mp_print_prologue (MP mp, pointer h) {
3996   font_number f;
3997   font_number  ldf ;
3998   int prologues = (mp->internal[mp_prologues]>>16);
3999   int procset = (mp->internal[mp_procset]>>16);
4000   ldf = mp_print_font_comments (mp, h);
4001   mp_print_ln(mp);
4002   if ( (mp->internal[mp_prologues]>0) && (mp->last_ps_fnum<mp->last_fnum) )
4003     mp_read_psname_table(mp);
4004   mp_print(mp, "%%BeginProlog"); mp_print_ln(mp);
4005   if ( (prologues>0)||(procset>0) ) {
4006     if ( ldf!=null_font ) {
4007       if ( prologues>0 ) {
4008         for (f=null_font+1;f<=mp->last_fnum;f++) {
4009           if ( mp_has_font_size(mp,f) ) {
4010             mp_ps_name_out(mp, mp->font_name[f],true);
4011             mp_ps_name_out(mp, mp->font_ps_name[f],true);
4012             mp_ps_print(mp, " def");
4013             mp_print_ln(mp);
4014           }
4015         }
4016         if ( procset==0 ) {
4017           mp_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4018           mp_print_ln(mp);
4019         }
4020       }
4021     }
4022     if (procset>0 ) {
4023       mp_print_nl(mp, "%%BeginResource: procset mpost");
4024       if ( (prologues>0)&&(ldf!=null_font) )
4025         mp_print(mp, 
4026         "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4027       else
4028         mp_print_nl(mp, "/bd{bind def}bind def");
4029       @<Print the procset@>;
4030       mp_print_nl(mp, "%%EndResource");
4031       mp_print_ln(mp);
4032     }
4033   }
4034   mp_print(mp, "%%EndProlog");
4035   mp_print_nl(mp, "%%Page: 1 1"); mp_print_ln(mp);
4036 }
4037
4038 @ Deciding where to break the ps output line. For the moment,
4039 it is necessary to create an exported function as well.
4040
4041 @d ps_room(A) if ( (mp->ps_offset+(int)(A))>mp->max_print_line ) 
4042   mp_print_ln(mp) /* optional line break */
4043
4044 @c
4045 void mp_ps_room (MP mp,int s) {
4046   ps_room(s);
4047 }
4048
4049 @ @<Exported...@>=
4050 void mp_ps_room (MP mp,int s) ;
4051
4052 @ \MP\ used to have one single routine to print to both `write' files
4053 and the PostScript output. Web2c redefines ``Character |k| cannot be
4054 printed'', and that resulted in some bugs where 8-bit characters were
4055 written to the PostScript file (reported by Wlodek Bzyl).
4056
4057 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4058 a plain space, since that makes it easier to parse the result file
4059 for postprocessing.
4060
4061 @<Character |k| is not allowed in PostScript output@>=
4062   (k<=' ')||(k>'~')
4063
4064 @ We often need to print a pair of coordinates.
4065
4066 @c
4067 void mp_ps_pair_out (MP mp,scaled x, scaled y) { 
4068   ps_room(26);
4069   mp_print_scaled(mp, x); mp_print_char(mp, ' ');
4070   mp_print_scaled(mp, y); mp_print_char(mp, ' ');
4071 }
4072
4073 @ @<Exported...@>=
4074 void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4075
4076 @ @c
4077 void mp_ps_print (MP mp,char *s) { 
4078    ps_room(strlen(s));
4079    mp_print(mp, s);
4080 }
4081 void mp_ps_print_cmd (MP mp, char *l, char *s) {
4082   if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_print(mp,s); }
4083   else { ps_room(strlen(l)); mp_print(mp, l); };
4084 }
4085
4086 @ @<Exported...@>=
4087 void mp_ps_print (MP mp,char *s) ;
4088 void mp_ps_print_cmd (MP mp, char *l, char *s) ;
4089
4090 @ @c
4091 void mp_ps_string_out (MP mp, char *s) {
4092   ASCII_code k; /* bits to be converted to octal */
4093   mp_print(mp, "(");
4094   while ((k=*s++)) {
4095     if ( mp->ps_offset+5>mp->max_print_line ) {
4096       mp_print_char(mp, '\\');
4097       mp_print_ln(mp);
4098     }
4099     if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4100       mp_print_char(mp, '\\');
4101       mp_print_char(mp, '0'+(k / 64));
4102       mp_print_char(mp, '0'+((k / 8) % 8));
4103       mp_print_char(mp, '0'+(k % 8));
4104     } else { 
4105       if ( (k=='(')||(k==')')||(k=='\\') ) mp_print_char(mp, '\\');
4106       mp_print_char(mp, k);
4107     }
4108   }
4109   mp_print_char(mp, ')');
4110 }
4111
4112 @ @<Exported...@>=
4113 void mp_ps_string_out (MP mp, char *s) ;
4114
4115 @ This is a define because the function does not use its |mp| argument.
4116
4117 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4118
4119 @c
4120 static boolean mp_do_is_ps_name (char *s) {
4121   ASCII_code k; /* the character being checked */
4122   while ((k=*s++)) {
4123     if ( (k<=' ')||(k>'~') ) return false;
4124     if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4125        (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4126   }
4127   return true;
4128 }
4129
4130 @ @<Exported...@>=
4131 void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4132
4133 @ @c
4134 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4135   ps_room(strlen(s)+2);
4136   mp_print_char(mp, ' ');
4137   if ( mp_is_ps_name(mp, s) ) {
4138     if ( lit ) mp_print_char(mp, '/');
4139       mp_print(mp, s);
4140   } else { 
4141     mp_ps_string_out(mp, s);
4142     if ( ! lit ) mp_ps_print(mp, "cvx ");
4143       mp_ps_print(mp, "cvn");
4144   }
4145 }
4146
4147
4148 @ These special comments described in the {\sl PostScript Language Reference
4149 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4150 We can't normally output ``conforming'' \ps\ because
4151 the structuring conventions don't allow us to say ``Please make sure the
4152 following characters are downloaded and define the \.{fshow} macro to access
4153 them.''
4154
4155 The exact bounding box is written out if |mp_prologues<0|, although this
4156 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4157 accurately. (Overfull boxes are avoided if an illustration is made to
4158 match a given \.{\char`\\hsize}.)
4159
4160 @<Exported...@>=
4161 void mp_print_initial_comment(MP mp, pointer h, 
4162                               scaled minx, scaled miny, scaled maxx, scaled maxy);
4163
4164 @ @c
4165 void mp_print_initial_comment(MP mp, pointer h, 
4166                               scaled minx, scaled miny, scaled maxx, scaled maxy) {
4167   scaled t;
4168   int prologues = (mp->internal[mp_prologues]>>16);
4169   mp_print(mp, "%!PS");
4170   if ( prologues>0 ) 
4171     mp_print(mp, "-Adobe-3.0 EPSF-3.0");
4172   mp_set_bbox(mp, h,true);
4173   mp_print_nl(mp, "%%BoundingBox: ");
4174   if ( minx>maxx) {
4175      mp_print(mp, "0 0 0 0");
4176   } else if ( prologues<0 ) {
4177     mp_ps_pair_out(mp, minx,miny);
4178     mp_ps_pair_out(mp, maxx,maxy);
4179   } else { 
4180     mp_ps_pair_out(mp, mp_floor_scaled(mp, minx),mp_floor_scaled(mp, miny));
4181     mp_ps_pair_out(mp, -mp_floor_scaled(mp, -maxx),-mp_floor_scaled(mp, -maxy));
4182   }
4183   mp_print_nl(mp, "%%HiResBoundingBox: ");
4184   if ( minx>maxx ) {
4185     mp_print(mp, "0 0 0 0");
4186   } else {
4187     mp_ps_pair_out(mp, minx,miny);
4188     mp_ps_pair_out(mp, maxx,maxy);
4189   }
4190   mp_print_nl(mp, "%%Creator: MetaPost ");
4191   mp_print(mp, mp_metapost_version(mp));
4192   mp_print_nl(mp, "%%CreationDate: ");
4193   mp_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year])); mp_print_char(mp, '.');
4194   mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month])); mp_print_char(mp, '.');
4195   mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day])); mp_print_char(mp, ':');
4196   t=mp_round_unscaled(mp, mp->internal[mp_time]);
4197   mp_print_dd(mp, t / 60); mp_print_dd(mp, t % 60);
4198   mp_print_nl(mp, "%%Pages: 1");
4199 }
4200