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