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