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