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