beta-0.89.2
[luatex.git] / source / texk / web2c / mplibdir / psout.w
blob496f5681bca98544dbd363215f1976c175a00179
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.
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@@>
28 \let\?=\relax % we want to be able to \write a \?
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.\ }}
35 \def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
36 \pdfoutput=1
37 \pageno=3
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| */
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 */
73 @<Declarations@>
74 @<Static variables in the outer block@>
76 @ There is a small bit of code from the backend that bleads through
77 to the frontend because I do not know how to set up the includes
78 properly. That is the |typedef struct psout_data_struct * psout_data|.
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...@>
88 typedef struct psout_data_struct {
89 @<Globals@>
90 } psout_data_struct ;
91 @<Exported function headers@>
92 #endif
94 @ @c
95 static boolean mp_isdigit (int a) {
96 return (a>='0'&&a<='9');
98 static int mp_tolower (int a) {
99 if (a>='A' && a <='Z')
100 return a - 'A' + 'a';
101 return a;
103 static 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++;
111 ss2 = mp_strdup(s2);
112 c = ss2;
113 while (*c != '\0') {
114 *c = (char)mp_tolower(*c); c++;
116 r = strcmp(ss1,ss2);
117 free (ss1); free(ss2);
118 return r;
121 @ @<Exported function headers@>=
122 void mp_ps_backend_initialize (MP mp) ;
123 void mp_ps_backend_free (MP mp) ;
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@>;
131 void 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;
140 @ Writing to ps files
142 @<Globals@>=
143 integer ps_offset;
144 /* the number of characters on the current \ps\ file line */
146 @ @<Set initial values@>=
147 mp->ps->ps_offset = 0;
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)); }
161 static void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
162 wps_cr;
163 mp->ps->ps_offset=0;
166 @ @c
167 static 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);
175 @ @c
176 static 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);
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++;
192 j++;
194 (mp->write_ascii_file)(mp,mp->output_file,outbuf);
198 @ Deciding where to break the ps output line.
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 */
205 static void mp_ps_print (MP mp, const char *ss) {
206 ps_room(strlen(ss));
207 mp_ps_do_print(mp, ss, strlen(ss));
209 static 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, ' ');
216 mp_ps_do_print(mp, ss, strlen(ss));
219 @ The procedure |print_nl| is like |print|, but it makes sure that the
220 string appears at the beginning of a new line.
223 static 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);
228 @ The following procedure, which prints out the decimal representation of a
229 given integer |n|, has been written carefully so that it works properly
230 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
231 to negative arguments, since such operations are not implemented consistently
232 by all \PASCAL\ compilers.
235 static 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);
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]);
261 outbuf[l] = '\0';
262 (mp->write_ascii_file)(mp,mp->output_file,outbuf);
265 @ \MP\ also makes use of a trivial procedure to print two digits. The
266 following subroutine is usually called with a parameter in the range |0<=n<=99|.
269 static 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));
275 @ Conversely, here is a procedure analogous to |print_int|.
277 There are two versions of this function: |ps_print_double_scaled| is used
278 if metapost runs in scaled (backward compatibility) mode, because that
279 version produces results that are much closer to the old version that exported
280 figures with scaled fields instead of double fields. It is not always the
281 same because a little bit of precision has gone in the scaled to double
282 conversion, but still quite a bit closer than |%.6f| in the 'double' case.
284 @d unityold 65536
286 static 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;
300 if (value[i] == '.')
301 value[i] = '\0';
302 c = value;
303 while (*c) {
304 mp_ps_print_char(mp, *c);
305 c++;
307 free(value);
310 static 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 */
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);
331 static 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);
340 @* \[44a] Dealing with font encodings.
342 First, here are a few helpers for parsing files
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);
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;
361 } while (0)
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--;
371 *p = 0;
372 } while (0)
374 @d remove_eol(p, buf) do {
375 p = strend(buf) - 1;
376 if (*p == 10)
377 *p = 0;
378 } while (0)
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)
385 @ @<Types...@>=
386 typedef 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;
398 @d ENC_STANDARD 0
399 @d ENC_BUILTIN 1
401 @<Glob...@>=
402 #define ENC_BUF_SIZE 0x1000
403 char enc_line[ENC_BUF_SIZE];
404 void * enc_file;
407 @d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file)
408 @d enc_close() (mp->close_file)(mp,mp->ps->enc_file)
411 static 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;
419 @ @c
420 static 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;
427 static void mp_enc_getline (MP mp) {
428 char *p;
429 int c;
430 RESTART:
431 if (enc_eof ()) {
432 mp_error(mp, "unexpected end of file", NULL, true);
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;
443 static 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;
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);
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);
483 if (mp_xstrcmp (buf, notdef) != 0)
484 glyph_names[names_count] = mp_xstrdup (mp,buf);
485 names_count++;
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);
497 mp_enc_getline (mp);
498 r = mp->ps->enc_line;
500 DONE:
501 enc_close ();
502 mp_print (mp,"}");
503 mp->selector = save_selector;
505 static 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;
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;
519 static 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;
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;
541 foffset += s+2;
542 mp_ps_print_char(mp,'/');
543 mp_ps_print(mp, g[i]);
544 mp_ps_print_char(mp,' ');
546 if (foffset>75)
547 mp_ps_print_ln (mp);
548 mp_ps_print_nl (mp,"] def\n");
549 mp_ps_print(mp,"%%%%EndResource");
553 @ All encoding entries go into AVL tree for fast search by name.
555 @<Glob...@>=
556 avl_tree enc_tree;
560 @<Static variables in the outer block@>=
561 static char notdef[] = ".notdef";
563 @ @<Set initial...@>=
564 mp->ps->enc_tree = NULL;
566 @ @c
567 static 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);
572 static 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;
587 @ Not having an |mp| instance here means that lots of |malloc| and
588 |strdup| checks are needed. Spotted by Peter Breitenlohner.
591 static 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;
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;
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;
623 return (void *)q;
626 static enc_entry * mp_add_enc (MP mp, char *s) {
627 int i;
628 enc_entry tmp, *p;
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);
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);
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);
655 @ cleaning up...
658 @ @<Declarations@>=
659 static void enc_free (MP mp);
661 @ @c static void enc_free (MP mp) {
662 if (mp->ps->enc_tree != NULL)
663 avl_destroy (mp->ps->enc_tree);
666 @ @<Declarations@>=
667 static void mp_reload_encodings (MP mp) ;
668 static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) ;
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;
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);
688 static 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;
708 @* \[44b] Parsing font map files.
710 @d FM_BUF_SIZE 1024
712 @<Glob...@>=
713 void * fm_file;
714 size_t fm_byte_waiting;
715 size_t fm_byte_length;
716 unsigned char *fm_bytes;
718 @ This is comparable to t1 font loading (see below) but because the first
719 thing done is not calling |fm_getchar()| but |fm_eof()|, the initial value
720 of length has to be one more than waiting.
722 @<Set initial ...@>=
723 mp->ps->fm_byte_waiting=0;
724 mp->ps->fm_byte_length=1;
725 mp->ps->fm_bytes=NULL;
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 @d unwrap_file(ff) ( mp->noninteractive ? ((File *) ff)->f : ff)
741 static int fm_getchar (MP mp) {
742 if (mp->ps->fm_bytes == NULL) {
743 void *byte_ptr ;
744 (void)fseek( unwrap_file(mp->ps->fm_file), 0,SEEK_END);
745 mp->ps->fm_byte_length = (size_t)ftell( unwrap_file(mp->ps->fm_file) );
746 (void)fseek( unwrap_file(mp->ps->fm_file), 0,SEEK_SET);
747 if (mp->ps->fm_byte_length==0)
748 return EOF;
749 mp->ps->fm_bytes = mp_xmalloc(mp, mp->ps->fm_byte_length, 1);
750 byte_ptr = (void *)mp->ps->fm_bytes;
751 (mp->read_binary_file)(mp, mp->ps->fm_file, &byte_ptr,&mp->ps->fm_byte_length);
753 if(mp->ps->fm_byte_waiting >= mp->ps->fm_byte_length)
754 return 10;
755 return *(mp->ps->fm_bytes+mp->ps->fm_byte_waiting++);
758 @ @<Types...@>=
759 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
760 enum _ltype { MAPFILE, MAPLINE };
761 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
762 typedef struct mitem {
763 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
764 int type; /* map file or map line */
765 char *map_line; /* pointer to map file name or map line */
766 int lineno; /* line number in map file */
767 } mapitem;
769 @ @<Glob...@>=
770 mapitem *mitem;
771 fm_entry *fm_cur;
772 fm_entry *loaded_tfm_found;
773 fm_entry *avail_tfm_found;
774 fm_entry *non_tfm_found;
775 fm_entry *not_avail_tfm_found;
777 @ @<Set initial...@>=
778 mp->ps->mitem = NULL;
780 @ @<Declarations@>=
781 static const char nontfm[] = "<nontfm>";
784 @d read_field(r, q, buf) do {
785 q = buf;
786 while (*r != ' ' && *r != '\0')
787 *q++ = *r++;
788 *q = '\0';
789 skip (r, ' ');
790 } while (0)
792 @d set_field(F) do {
793 if (q > buf)
794 fm->F = mp_xstrdup(mp,buf);
795 if (*r == '\0')
796 goto DONE;
797 } while (0)
799 @d cmp_return(a, b)
800 if (a > b)
801 return 1;
802 if (a < b)
803 return -1
805 @d do_strdup(a) (a==NULL ? NULL : strdup(a))
808 static fm_entry *new_fm_entry (MP mp) {
809 fm_entry *fm;
810 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
811 fm->tfm_name = NULL;
812 fm->ps_name = NULL;
813 fm->flags = 4;
814 fm->ff_name = NULL;
815 fm->subset_tag = NULL;
816 fm->encoding = NULL;
817 fm->tfm_num = null_font;
818 fm->tfm_avail = TFM_UNCHECKED;
819 fm->type = 0;
820 fm->slant = 0;
821 fm->extend = 0;
822 fm->ff_objnum = 0;
823 fm->fn_objnum = 0;
824 fm->fd_objnum = 0;
825 fm->charset = NULL;
826 fm->all_glyphs = false;
827 fm->links = 0;
828 fm->pid = -1;
829 fm->eid = -1;
830 return fm;
833 static void *copy_fm_entry (const void *p) {
834 fm_entry *fm;
835 const fm_entry *fp;
836 fp = (const fm_entry *)p;
837 fm = malloc (sizeof(fm_entry));
838 if (fm==NULL)
839 return NULL;
840 memcpy(fm, fp, sizeof(fm_entry));
841 fm->tfm_name = do_strdup(fp->tfm_name);
842 fm->ps_name = do_strdup(fp->ps_name);
843 fm->ff_name = do_strdup(fp->ff_name);
844 fm->subset_tag = do_strdup(fp->subset_tag);
845 fm->charset = do_strdup(fp->charset);
846 return (void *)fm;
850 static void * delete_fm_entry (void *p) {
851 fm_entry *fm = (fm_entry *)p;
852 mp_xfree (fm->tfm_name);
853 mp_xfree (fm->ps_name);
854 mp_xfree (fm->ff_name);
855 mp_xfree (fm->subset_tag);
856 mp_xfree (fm->charset);
857 mp_xfree (fm);
858 return NULL;
861 static ff_entry *new_ff_entry (MP mp) {
862 ff_entry *ff;
863 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
864 ff->ff_name = NULL;
865 ff->ff_path = NULL;
866 return ff;
869 static void *copy_ff_entry (const void *p) {
870 ff_entry *ff;
871 const ff_entry *fp;
872 fp = (const ff_entry *)p;
873 ff = (ff_entry *)malloc (sizeof(ff_entry));
874 if (ff == NULL)
875 return NULL;
876 ff->ff_name = do_strdup(fp->ff_name);
877 ff->ff_path = do_strdup(fp->ff_path);
878 return ff;
881 static void * delete_ff_entry (void *p) {
882 ff_entry *ff = (ff_entry *)p;
883 mp_xfree (ff->ff_name);
884 mp_xfree (ff->ff_path);
885 mp_xfree (ff);
886 return NULL;
889 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
890 static char buf[SMALL_BUF_SIZE];
891 char *p = tfmname, *r = strend (p) - 1, *q = r;
892 while (q > p && mp_isdigit (*q))
893 --q;
894 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
895 return NULL;
896 check_buf (q - p + 1, SMALL_BUF_SIZE);
897 strncpy (buf, p, (size_t) (q - p));
898 buf[q - p] = '\0';
899 *i = atoi (q);
900 return buf;
903 @ @<Declarations@>=
904 static boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
906 @ @c
907 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
908 fm_entry *res = NULL;
909 res = mp_fm_lookup (mp, f);
910 if (fm != NULL) {
911 *fm =res;
913 return (res != NULL);
916 @ @<Glob...@>=
917 avl_tree tfm_tree;
918 avl_tree ps_tree;
919 avl_tree ff_tree;
921 @ @<Set initial...@>=
922 mp->ps->tfm_tree = NULL;
923 mp->ps->ps_tree = NULL;
924 mp->ps->ff_tree = NULL;
926 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
929 static int comp_fm_entry_tfm (void *p, const void *pa, const void *pb) {
930 (void)p;
931 return strcmp (((const fm_entry *) pa)->tfm_name,
932 ((const fm_entry *) pb)->tfm_name);
935 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
937 @c static int comp_fm_entry_ps (void *p, const void *pa, const void *pb) {
938 int i;
939 const fm_entry *p1 = (const fm_entry *) pa;
940 const fm_entry *p2 = (const fm_entry *) pb;
941 (void)p;
942 assert (p1->ps_name != NULL && p2->ps_name != NULL);
943 if ((i = strcmp (p1->ps_name, p2->ps_name)))
944 return i;
945 cmp_return (p1->slant, p2->slant);
946 cmp_return (p1->extend, p2->extend);
947 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
948 (i = strcmp (p1->tfm_name, p2->tfm_name)))
949 return i;
950 return 0;
953 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
955 @c static int comp_ff_entry (void *p, const void *pa, const void *pb) {
956 (void)p;
957 return strcmp (((const ff_entry *) pa)->ff_name,
958 ((const ff_entry *) pb)->ff_name);
961 @ @c static void create_avl_trees (MP mp) {
962 if (mp->ps->tfm_tree == NULL) {
963 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm,
964 copy_fm_entry,
965 delete_fm_entry,
966 malloc, free, NULL);
967 assert (mp->ps->tfm_tree != NULL);
969 if (mp->ps->ps_tree == NULL) {
970 mp->ps->ps_tree = avl_create (comp_fm_entry_ps,
971 copy_fm_entry,
972 delete_fm_entry,
973 malloc, free, NULL);
974 assert (mp->ps->ps_tree != NULL);
976 if (mp->ps->ff_tree == NULL) {
977 mp->ps->ff_tree = avl_create (comp_ff_entry,
978 copy_ff_entry,
979 delete_ff_entry,
980 malloc, free, NULL);
981 assert (mp->ps->ff_tree != NULL);
985 @ The function |avl_do_entry| is not completely symmetrical with regards
986 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
987 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
988 with the original version.
990 @d LINK_TFM 0x01
991 @d LINK_PS 0x02
992 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
993 @d set_pslink(fm) ((fm)->links |= LINK_PS)
994 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
995 @d has_pslink(fm) ((fm)->links & LINK_PS)
998 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
999 fm_entry *p;
1000 char s[128];
1001 /* handle |tfm_name| link */
1003 if (strcmp (fp->tfm_name, nontfm)) {
1004 p = (fm_entry *) avl_find (fp, mp->ps->tfm_tree);
1005 if (p != NULL) {
1006 if (mode == FM_DUPIGNORE) {
1007 mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
1008 fp->tfm_name);
1009 mp_warn(mp,s);
1010 goto exit;
1011 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
1012 if (mp_has_font_size(mp,p->tfm_num)) {
1013 mp_snprintf(s,128,
1014 "fontmap entry for `%s' has been used, replace/delete not allowed",
1015 fp->tfm_name);
1016 mp_warn(mp,s);
1017 goto exit;
1019 (void) avl_del (p,mp->ps->tfm_tree,NULL);
1020 p = NULL;
1023 if (mode != FM_DELETE) {
1024 if (p==NULL) {
1025 assert (avl_ins(fp, mp->ps->tfm_tree, avl_false)>0);
1027 set_tfmlink (fp);
1031 /* handle |ps_name| link */
1033 if (fp->ps_name != NULL) {
1034 assert (fp->tfm_name != NULL);
1035 p = (fm_entry *) avl_find (fp, mp->ps->ps_tree);
1036 if (p != NULL) {
1037 if (mode == FM_DUPIGNORE) {
1038 mp_snprintf(s,128,
1039 "ps_name entry for `%s' already exists, duplicates ignored",
1040 fp->ps_name);
1041 mp_warn(mp,s);
1042 goto exit;
1043 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
1044 if (mp_has_font_size(mp,p->tfm_num)) {
1045 /* REPLACE/DELETE not allowed */
1046 mp_snprintf(s,128,
1047 "fontmap entry for `%s' has been used, replace/delete not allowed",
1048 p->tfm_name);
1049 mp_warn(mp,s);
1050 goto exit;
1052 (void)avl_del (p,mp->ps->ps_tree,NULL);
1053 p= NULL;
1056 if (mode != FM_DELETE) {
1057 if (p==NULL) {
1058 assert (avl_ins(fp, mp->ps->ps_tree, avl_false)>0);
1060 set_pslink (fp);
1063 exit:
1064 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
1065 return 1; /* deallocation of |fm_entry| structure required */
1066 else
1067 return 0;
1070 @ consistency check for map entry, with warn flag
1073 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
1074 int a = 0;
1075 char s[128];
1076 assert (fm != NULL);
1077 if (fm->ps_name != NULL) {
1078 if (is_basefont (fm)) {
1079 if (is_fontfile (fm) && !is_included (fm)) {
1080 if (warn) {
1081 mp_snprintf(s,128, "invalid entry for `%s': "
1082 "font file must be included or omitted for base fonts",
1083 fm->tfm_name);
1084 mp_warn(mp,s);
1086 a += 1;
1088 } else { /* not a base font */
1089 /* if no font file given, drop this entry */
1090 /* |if (!is_fontfile (fm)) {
1091 if (warn) {
1092 mp_snprintf(s,128,
1093 "invalid entry for `%s': font file missing",
1094 fm->tfm_name);
1095 mp_warn(mp,s);
1097 a += 2;
1102 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
1103 if (warn) {
1104 mp_snprintf(s,128,
1105 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
1106 fm->tfm_name);
1107 mp_warn(mp,s);
1109 a += 4;
1111 if ((fm->slant != 0 || fm->extend != 0) &&
1112 (is_truetype (fm))) {
1113 if (warn) {
1114 mp_snprintf(s,128,
1115 "invalid entry for `%s': "
1116 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
1117 fm->tfm_name);
1118 mp_warn(mp,s);
1120 a += 8;
1122 if (abs (fm->slant) > 1000) {
1123 if (warn) {
1124 mp_snprintf(s,128,
1125 "invalid entry for `%s': too big value of SlantFont (%d/1000.0)",
1126 fm->tfm_name, (int)fm->slant);
1127 mp_warn(mp,s);
1129 a += 16;
1131 if (abs (fm->extend) > 2000) {
1132 if (warn) {
1133 mp_snprintf(s,128,
1134 "invalid entry for `%s': too big value of ExtendFont (%d/1000.0)",
1135 fm->tfm_name, (int)fm->extend);
1136 mp_warn(mp,s);
1138 a += 32;
1140 if (fm->pid != -1 &&
1141 !(is_truetype (fm) && is_included (fm) &&
1142 is_subsetted (fm) && !is_reencoded (fm))) {
1143 if (warn) {
1144 mp_snprintf(s,128,
1145 "invalid entry for `%s': "
1146 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
1147 fm->tfm_name);
1148 mp_warn(mp,s);
1150 a += 64;
1152 return a;
1155 @ returns true if s is one of the 14 std. font names; speed-trimmed.
1157 @c static boolean check_basefont (char *s) {
1158 static const char *basefont_names[] = {
1159 "Courier", /* 0:7 */
1160 "Courier-Bold", /* 1:12 */
1161 "Courier-Oblique", /* 2:15 */
1162 "Courier-BoldOblique", /* 3:19 */
1163 "Helvetica", /* 4:9 */
1164 "Helvetica-Bold", /* 5:14 */
1165 "Helvetica-Oblique", /* 6:17 */
1166 "Helvetica-BoldOblique", /* 7:21 */
1167 "Symbol", /* 8:6 */
1168 "Times-Roman", /* 9:11 */
1169 "Times-Bold", /* 10:10 */
1170 "Times-Italic", /* 11:12 */
1171 "Times-BoldItalic", /* 12:16 */
1172 "ZapfDingbats" /* 13:12 */
1174 static const int Index[] =
1175 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1176 -1, 3, -1, 7
1178 const size_t n = strlen (s);
1179 int k = -1;
1180 if (n > 21)
1181 return false;
1182 if (n == 12) { /* three names have length 12 */
1183 switch (*s) {
1184 case 'C':
1185 k = 1; /* Courier-Bold */
1186 break;
1187 case 'T':
1188 k = 11; /* Times-Italic */
1189 break;
1190 case 'Z':
1191 k = 13; /* ZapfDingbats */
1192 break;
1193 default:
1194 return false;
1196 } else
1197 k = Index[n];
1198 if (k > -1 && !strcmp (basefont_names[k], s))
1199 return true;
1200 return false;
1204 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1206 @c static void fm_scan_line (MP mp) {
1207 int a, b, c, j, u = 0, v = 0;
1208 float d;
1209 fm_entry *fm;
1210 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1211 char *p, *q, *s;
1212 char warn_s[128];
1213 char *r = NULL;
1214 switch (mp->ps->mitem->type) {
1215 case MAPFILE:
1216 p = fm_line;
1217 do {
1218 c = fm_getchar (mp);
1219 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1220 } while (c != 10);
1221 *(--p) = '\0';
1222 r = fm_line;
1223 break;
1224 case MAPLINE:
1225 r = mp->ps->mitem->map_line;
1226 break;
1227 default:
1228 assert (0);
1230 if (*r == '\0' || is_cfg_comment (*r))
1231 return;
1232 fm = new_fm_entry (mp);
1233 read_field (r, q, buf);
1234 set_field (tfm_name);
1235 p = r;
1236 read_field (r, q, buf);
1237 if (*buf != '<' && *buf != '"')
1238 set_field (ps_name);
1239 else
1240 r = p; /* unget the field */
1241 if (mp_isdigit (*r)) { /* font flags given */
1242 fm->flags = atoi (r);
1243 while (mp_isdigit (*r))
1244 r++;
1246 if(fm->ps_name == NULL)
1247 fm->ps_name = xstrdup(fm->tfm_name);
1248 while (1) { /* loop through "specials", encoding, font file */
1249 skip (r, ' ');
1250 switch (*r) {
1251 case '\0':
1252 goto DONE;
1253 case '"': /* opening quote */
1254 r++;
1255 u = v = 0;
1256 do {
1257 skip (r, ' ');
1258 if (sscanf (r, "%f %n", &d, &j) > 0) {
1259 s = r + j; /* jump behind number, eat also blanks, if any */
1260 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1261 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1262 if (str_prefix (s, "SlantFont")) {
1263 d *= (float)1000.0; /* correct rounding also for neg. numbers */
1264 fm->slant = (short int) (d > 0 ? d + 0.5 : d - 0.5);
1265 r = s + strlen ("SlantFont");
1266 } else if (str_prefix (s, "ExtendFont")) {
1267 d *= (float)1000.0;
1268 fm->extend = (short int) (d > 0 ? d + 0.5 : d - 0.5);
1269 if (fm->extend == 1000)
1270 fm->extend = 0;
1271 r = s + strlen ("ExtendFont");
1272 } else { /* unknown name */
1273 for (r = s;
1274 *r != ' ' && *r != '"' && *r != '\0';
1275 r++); /* jump over name */
1276 c = *r; /* remember char for temporary end of string */
1277 *r = '\0';
1278 mp_snprintf(warn_s,128,
1279 "invalid entry for `%s': unknown name `%s' ignored",
1280 fm->tfm_name, s);
1281 mp_warn(mp,warn_s);
1282 *r = (char)c;
1284 } else
1285 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1287 while (*r == ' ');
1288 if (*r == '"') /* closing quote */
1289 r++;
1290 else {
1291 mp_snprintf(warn_s,128,
1292 "invalid entry for `%s': closing quote missing",
1293 fm->tfm_name);
1294 mp_warn(mp,warn_s);
1295 goto bad_line;
1297 break;
1298 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1299 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1300 fm->pid = (short int)a;
1301 fm->eid = (short int)b;
1302 r += c;
1303 break;
1304 } /* fallthrough */
1305 default: /* encoding or font file specification */
1306 a = b = 0;
1307 if (*r == '<') {
1308 a = *r++;
1309 if (*r == '<' || *r == '[')
1310 b = *r++;
1312 read_field (r, q, buf);
1313 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1314 if (strlen (buf) > 4 && mp_strcasecmp (strend (buf) - 4, ".enc") == 0) {
1315 fm->encoding = mp_add_enc (mp, buf);
1316 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1317 } else if (strlen (buf) > 0) { /* file name given */
1318 /* font file, formats:
1319 * subsetting: '<cmr10.pfa'
1320 * no subsetting: '<<cmr10.pfa'
1321 * no embedding: 'cmr10.pfa'
1323 if (a == '<' || u == '<') {
1324 set_included (fm);
1325 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1326 set_subsetted (fm);
1327 /* otherwise b == '<' (or '[') => no subsetting */
1329 set_field (ff_name);
1330 u = v = 0;
1331 } else {
1332 u = a;
1333 v = b;
1337 DONE:
1338 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1339 set_basefont (fm);
1340 if (is_fontfile (fm)
1341 && mp_strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1342 set_truetype (fm);
1343 if (check_fm_entry (mp,fm, true) != 0)
1344 goto bad_line;
1346 Until here the map line has been completely scanned without errors;
1347 fm points to a valid, freshly filled-out |fm_entry| structure.
1348 Now follows the actual work of registering/deleting.
1350 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) { /* if success */
1351 delete_fm_entry (fm);
1352 return;
1354 bad_line:
1355 delete_fm_entry (fm);
1359 @c static void fm_read_info (MP mp) {
1360 char *n;
1361 char s[256];
1362 if (mp->ps->tfm_tree == NULL)
1363 create_avl_trees (mp);
1364 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1365 return;
1366 mp->ps->mitem->lineno = 1;
1367 switch (mp->ps->mitem->type) {
1368 case MAPFILE:
1369 n = mp->ps->mitem->map_line;
1370 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1371 if (!mp->ps->fm_file) {
1372 mp_snprintf(s,256,"cannot open font map file %s",n);
1373 mp_warn(mp,s);
1374 } else {
1375 unsigned save_selector = mp->selector;
1376 mp_normalize_selector(mp);
1377 mp_print (mp, "{");
1378 mp_print (mp, n);
1379 while (!fm_eof ()) {
1380 fm_scan_line (mp);
1381 mp->ps->mitem->lineno++;
1383 fm_close ();
1384 mp_print (mp,"}");
1385 mp->selector = save_selector;
1386 mp->ps->fm_file = NULL;
1388 /* mp_xfree(n); */
1389 break;
1390 case MAPLINE:
1391 fm_scan_line (mp);
1392 break;
1393 default:
1394 assert (0);
1396 mp->ps->mitem->map_line = NULL; /* done with this line */
1397 return;
1400 @ @c static void init_fm (fm_entry * fm, font_number f) {
1401 if (fm->tfm_num == null_font ) {
1402 fm->tfm_num = f;
1403 fm->tfm_avail = TFM_FOUND;
1407 @ @<Exported function ...@>=
1408 fm_entry * mp_fm_lookup (MP mp, font_number f);
1410 @ @c
1411 fm_entry * mp_fm_lookup (MP mp, font_number f) {
1412 char *tfm;
1413 fm_entry *fm;
1414 fm_entry tmp;
1415 int e;
1416 if (mp->ps->tfm_tree == NULL)
1417 mp_read_psname_table (mp); /* only to read default map file */
1418 tfm = mp->font_name[f];
1419 assert (strcmp (tfm, nontfm));
1420 /* Look up for full <tfmname>[+-]<expand> */
1421 tmp.tfm_name = tfm;
1422 fm = (fm_entry *) avl_find (&tmp, mp->ps->tfm_tree);
1423 if (fm != NULL) {
1424 init_fm (fm, f);
1425 return (fm_entry *) fm;
1427 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1428 if (tfm == NULL) /* not an expanded font, nothing to do */
1429 return NULL;
1431 tmp.tfm_name = tfm;
1432 fm = (fm_entry *) avl_find (&tmp, mp->ps->tfm_tree);
1433 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1434 return (fm_entry *) fm; /* font expansion uses the base font */
1436 return NULL;
1439 @ Early check whether a font file exists. Used e. g. for replacing fonts
1440 of embedded PDF files: Without font file, the font within the embedded
1441 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1442 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1443 only once per font file name + expansion parameter. This might help
1444 keeping speed, if many PDF pages with same fonts are to be embedded.
1446 The |ff_tree| contains only font files, which are actually needed,
1447 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1450 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1451 ff_entry *ff;
1452 ff_entry tmp;
1454 assert (fm->ff_name != NULL);
1455 tmp.ff_name = fm->ff_name;
1456 ff = (ff_entry *) avl_find (&tmp, mp->ps->ff_tree);
1457 if (ff == NULL) { /* not yet in database */
1458 ff = new_ff_entry (mp);
1459 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1460 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1461 assert(avl_ins (ff, mp->ps->ff_tree, avl_false)>0);
1462 delete_ff_entry(ff);
1463 ff = (ff_entry *) avl_find (&tmp, mp->ps->ff_tree);
1465 return ff;
1468 @ Process map file given by its name or map line contents. Items not
1469 beginning with [+-=] flush default map file, if it has not yet been
1470 read. Leading blanks and blanks immediately following [+-=] are ignored.
1473 @c static void mp_process_map_item (MP mp, char *s, int type) {
1474 char *p;
1475 int mode;
1476 if (*s == ' ')
1477 s++; /* ignore leading blank */
1478 switch (*s) {
1479 case '+': /* +mapfile.map, +mapline */
1480 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1481 s++;
1482 break;
1483 case '=': /* =mapfile.map, =mapline */
1484 mode = FM_REPLACE; /* try to replace earlier entry */
1485 s++;
1486 break;
1487 case '-': /* -mapfile.map, -mapline */
1488 mode = FM_DELETE; /* try to delete entry */
1489 s++;
1490 break;
1491 default:
1492 mode = FM_DUPIGNORE; /* like +, but also: */
1493 mp_xfree(mp->ps->mitem->map_line);
1494 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1496 if (*s == ' ')
1497 s++; /* ignore blank after [+-=] */
1498 p = s; /* map item starts here */
1499 switch (type) {
1500 case MAPFILE: /* remove blank at end */
1501 while (*p != '\0' && *p != ' ')
1502 p++;
1503 *p = '\0';
1504 break;
1505 case MAPLINE: /* blank at end allowed */
1506 break;
1507 default:
1508 assert (0);
1510 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1511 fm_read_info (mp);
1512 if (*s != '\0') { /* only if real item to process */
1513 mp->ps->mitem->mode = mode;
1514 mp->ps->mitem->type = type;
1515 mp->ps->mitem->map_line = s;
1516 fm_read_info (mp);
1520 @ @<Exported function headers@>=
1521 void mp_map_file (MP mp, mp_string t);
1522 void mp_map_line (MP mp, mp_string t);
1523 void mp_init_map_file (MP mp, int is_troff);
1525 @ @c
1526 void mp_map_file (MP mp, mp_string t) {
1527 char *ss = mp_str (mp,t);
1528 char *s = mp_xstrdup(mp, ss);
1529 mp_process_map_item (mp, s, MAPFILE);
1531 void mp_map_line (MP mp, mp_string t) {
1532 char *ss = mp_str (mp,t);
1533 char *s = mp_xstrdup(mp,ss);
1534 mp_process_map_item (mp, s, MAPLINE);
1535 mp_xfree(s);
1539 @c void mp_init_map_file (MP mp, int is_troff) {
1540 char *r;
1541 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1542 mp->ps->mitem->mode = FM_DUPIGNORE;
1543 mp->ps->mitem->type = MAPFILE;
1544 mp->ps->mitem->map_line = NULL;
1545 r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap);
1546 if (r != NULL) {
1547 mp_xfree(r);
1548 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1549 } else {
1550 if (is_troff) {
1551 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1552 } else {
1553 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1558 @ @<Dealloc variables@>=
1559 if (mp->ps->mitem!=NULL) {
1560 mp_xfree(mp->ps->mitem->map_line);
1561 mp_xfree(mp->ps->mitem);
1564 @ @<Declarations@>=
1565 static void fm_free (MP mp);
1567 @ @c
1568 static void fm_free (MP mp) {
1569 if (mp->ps->tfm_tree != NULL)
1570 avl_destroy (mp->ps->tfm_tree);
1571 if (mp->ps->ps_tree != NULL)
1572 avl_destroy (mp->ps->ps_tree);
1573 if (mp->ps->ff_tree != NULL)
1574 avl_destroy (mp->ps->ff_tree);
1577 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1578 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1579 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
1580 one or more spaces, a PostScript name, and possibly a space and some other junk.
1581 This routine reads the table, updates |font_ps_name| entries starting after
1582 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
1584 @d ps_tab_name "psfonts.map" /* locates font name translation table */
1586 @<Exported function ...@>=
1587 void mp_read_psname_table (MP mp) ;
1589 @ @c
1590 void mp_read_psname_table (MP mp) {
1591 font_number k;
1592 char *s;
1593 static int isread = 0;
1594 if (mp->ps->mitem == NULL) {
1595 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1596 mp->ps->mitem->mode = FM_DUPIGNORE;
1597 mp->ps->mitem->type = MAPFILE;
1598 mp->ps->mitem->map_line = NULL;
1600 s = mp_xstrdup (mp,ps_tab_name);
1601 mp->ps->mitem->map_line = s;
1602 if (isread == 0) {
1603 isread++;
1604 fm_read_info (mp);
1606 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1607 if (mp_has_fm_entry(mp, k, NULL)) {
1608 mp_xfree(mp->font_ps_name[k]);
1609 mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1612 mp->last_ps_fnum=mp->last_fnum;
1616 @ The traditional function is a lot shorter now.
1620 @* \[44c] Helper functions for Type1 fonts.
1622 Avoid to redefine |Byte| and |Bytef| from |<zlib.h>|.
1624 @<Types...@>=
1625 typedef char char_entry;
1626 #ifndef ZCONF_H
1627 typedef unsigned char Byte;
1628 typedef Byte Bytef;
1629 #endif
1631 @ @<Glob...@>=
1632 char_entry *char_ptr, *char_array;
1633 size_t char_limit;
1634 char *job_id_string;
1636 @ @<Set initial...@>=
1637 mp->ps->char_array = NULL;
1638 mp->ps->job_id_string = NULL;
1641 @d SMALL_ARRAY_SIZE 256
1642 @d Z_NULL 0
1645 void mp_set_job_id (MP mp) {
1646 char *name_string, *s;
1647 size_t slen;
1648 if (mp->ps->job_id_string != NULL)
1649 return;
1650 if ( mp->job_name==NULL )
1651 mp->job_name = mp_xstrdup(mp,"mpout");
1652 name_string = mp_xstrdup (mp,mp->job_name);
1653 slen = SMALL_BUF_SIZE +
1654 strlen (name_string) ;
1655 s = mp_xmalloc (mp,slen, sizeof (char));
1656 @= /*@@-bufferoverflowhigh@@*/ @>
1657 sprintf (s,"%.4u/%.2u/%.2u %.2u:%.2u %s",
1658 ((unsigned)number_to_scaled (internal_value(mp_year))>>16),
1659 ((unsigned)number_to_scaled (internal_value(mp_month))>>16),
1660 ((unsigned)number_to_scaled (internal_value(mp_day))>>16),
1661 ((unsigned)number_to_scaled (internal_value(mp_time))>>16) / 60,
1662 ((unsigned)number_to_scaled (internal_value(mp_time))>>16) % 60,
1663 name_string);
1664 @= /*@@=bufferoverflowhigh@@*/ @>
1665 mp->ps->job_id_string = mp_xstrdup (mp,s);
1666 mp_xfree (s);
1667 mp_xfree (name_string);
1669 static void fnstr_append (MP mp, const char *ss) {
1670 size_t n = strlen (ss) + 1;
1671 alloc_array (char, n, SMALL_ARRAY_SIZE);
1672 strcat (mp->ps->char_ptr, ss);
1673 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1676 @ @<Exported function headers@>=
1677 void mp_set_job_id (MP mp) ;
1679 @ @<Dealloc variables@>=
1680 mp_xfree(mp->ps->job_id_string);
1682 @ this is not really a true crc32, but it should be just enough to keep
1683 subsets prefixes somewhat disjunct
1686 static unsigned long crc32 (unsigned long oldcrc, const Byte *buf, size_t len) {
1687 unsigned long ret = 0;
1688 size_t i;
1689 if (oldcrc==0)
1690 ret = (unsigned long)((23<<24)+(45<<16)+(67<<8)+89);
1691 else
1692 for (i=0;i<len;i++)
1693 ret = (ret<<2)+buf[i];
1694 return ret;
1696 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1697 integer b; /* |char_base[f]| */
1698 b=mp->char_base[f];
1699 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1700 return true;
1701 else
1702 return false;
1705 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, font_number tex_font)
1707 char tag[7];
1708 unsigned long crc;
1709 int i;
1710 size_t l ;
1711 if (mp->ps->job_id_string ==NULL)
1712 mp_fatal_error(mp, "no job id!");
1713 l = strlen (mp->ps->job_id_string) + 1;
1715 alloc_array (char, l, SMALL_ARRAY_SIZE);
1716 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1717 mp->ps->char_ptr = strend (mp->ps->char_array);
1718 if (fm_cur->tfm_name != NULL) {
1719 fnstr_append (mp," TFM name: ");
1720 fnstr_append (mp,fm_cur->tfm_name);
1722 fnstr_append (mp," PS name: ");
1723 if (fm_cur->ps_name != NULL)
1724 fnstr_append (mp,fm_cur->ps_name);
1725 fnstr_append (mp," Encoding: ");
1726 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1727 fnstr_append (mp,(fm_cur->encoding)->file_name);
1728 else
1729 fnstr_append (mp,"built-in");
1730 fnstr_append (mp," CharSet: ");
1731 for (i = 0; i < 256; i++)
1732 if (mp_char_marked (mp,tex_font, (eight_bits)i) &&
1733 glyph_names[i] != notdef &&
1734 strcmp(glyph_names[i],notdef) != 0) {
1735 if (glyph_names[i]!=NULL) {
1736 fnstr_append (mp,"/");
1737 fnstr_append (mp,glyph_names[i]);
1740 if (fm_cur->charset != NULL) {
1741 fnstr_append (mp," Extra CharSet: ");
1742 fnstr_append (mp, fm_cur->charset);
1744 crc = crc32 (0L, Z_NULL, 0);
1745 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1746 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1747 * there are 26 uppercase chars ==> each char represents a number in range
1748 * |0..25|. The maximal number that can be represented by the tag is
1749 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1750 * of the CRC must be dropped out.
1752 for (i = 0; i < 6; i++) {
1753 tag[i] = (char)('A' + crc % 26);
1754 crc /= 26;
1756 tag[6] = 0;
1757 mp_xfree(fm_cur->subset_tag);
1758 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1764 @d external_enc() (fm_cur->encoding)->glyph_names
1765 @d is_used_char(c) mp_char_marked (mp, tex_font, (eight_bits)c)
1766 @d end_last_eexec_line()
1767 mp->ps->hexline_length = HEXLINE_WIDTH;
1768 end_hexline(mp);
1769 mp->ps->t1_eexec_encrypt = false
1770 @d t1_log(s) mp_print(mp,s)
1771 @d t1_putchar(c) wps_chr(c)
1772 @d embed_all_glyphs(tex_font) false
1773 @d t1_char(c) c
1774 @d extra_charset() mp->ps->dvips_extra_charset
1775 @d update_subset_tag()
1776 @d fixedcontent true
1778 @<Glob...@>=
1779 #define PRINTF_BUF_SIZE 1024
1780 char *dvips_extra_charset;
1781 char *cur_enc_name;
1782 unsigned char *grid;
1783 char *ext_glyph_names[256];
1784 char print_buf[PRINTF_BUF_SIZE];
1785 size_t t1_byte_waiting;
1786 size_t t1_byte_length;
1787 unsigned char *t1_bytes;
1789 @ @<Set initial ...@>=
1790 mp->ps->dvips_extra_charset=NULL;
1791 mp->ps->t1_byte_waiting=0;
1792 mp->ps->t1_byte_length=0;
1793 mp->ps->t1_bytes=NULL;
1796 @d t1_ungetchar() mp->ps->t1_byte_waiting--
1797 @d t1_eof() (mp->ps->t1_byte_waiting>=mp->ps->t1_byte_length)
1798 @d t1_close() do {
1799 (mp->close_file)(mp,mp->ps->t1_file);
1800 mp_xfree(mp->ps->t1_bytes);
1801 mp->ps->t1_bytes = NULL;
1802 mp->ps->t1_byte_waiting=0;
1803 mp->ps->t1_byte_length=0;
1804 } while (0)
1805 @d valid_code(c) (c >= 0 && c < 256)
1808 static int t1_getchar (MP mp) {
1809 if (mp->ps->t1_bytes == NULL) {
1810 void *byte_ptr ;
1811 (void)fseek( unwrap_file(mp->ps->t1_file), 0,SEEK_END);
1812 mp->ps->t1_byte_length = (size_t)ftell( unwrap_file(mp->ps->t1_file) );
1813 (void)fseek( unwrap_file(mp->ps->t1_file), 0,SEEK_SET);
1814 mp->ps->t1_bytes = mp_xmalloc(mp, mp->ps->t1_byte_length, 1);
1815 byte_ptr = (void *)mp->ps->t1_bytes;
1816 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&mp->ps->t1_byte_length);
1818 return *(mp->ps->t1_bytes+mp->ps->t1_byte_waiting++);
1821 @ @<Static variables in the outer block@>=
1822 static const char *standard_glyph_names[256] =
1823 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1824 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1825 notdef, notdef, notdef, notdef, notdef, notdef,
1826 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1827 "space", "exclam", "quotedbl", "numbersign",
1828 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1829 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1830 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1831 "eight", "nine", "colon", "semicolon", "less",
1832 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1833 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1834 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1835 "backslash", "bracketright", "asciicircum", "underscore",
1836 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1837 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1838 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1839 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1840 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1841 notdef, notdef, notdef, notdef, notdef, notdef,
1842 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1843 notdef, notdef, notdef, "exclamdown", "cent",
1844 "sterling", "fraction", "yen", "florin", "section", "currency",
1845 "quotesingle", "quotedblleft", "guillemotleft",
1846 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1847 "dagger", "daggerdbl", "periodcentered", notdef,
1848 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1849 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1850 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1851 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1852 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1853 notdef, notdef, notdef, notdef, notdef, notdef,
1854 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1855 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1856 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1857 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1858 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1859 "germandbls", notdef, notdef, notdef, notdef };
1860 static const char charstringname[] = "/CharStrings";
1862 @ @<Glob...@>=
1863 char **t1_glyph_names;
1864 char *t1_builtin_glyph_names[256];
1865 char charsetstr[0x4000];
1866 boolean read_encoding_only;
1867 int t1_encoding;
1869 @ @c
1870 #define T1_BUF_SIZE 0x100
1872 #define CS_HSTEM 1
1873 #define CS_VSTEM 3
1874 #define CS_VMOVETO 4
1875 #define CS_RLINETO 5
1876 #define CS_HLINETO 6
1877 #define CS_VLINETO 7
1878 #define CS_RRCURVETO 8
1879 #define CS_CLOSEPATH 9
1880 #define CS_CALLSUBR 10
1881 #define CS_RETURN 11
1882 #define CS_ESCAPE 12
1883 #define CS_HSBW 13
1884 #define CS_ENDCHAR 14
1885 #define CS_RMOVETO 21
1886 #define CS_HMOVETO 22
1887 #define CS_VHCURVETO 30
1888 #define CS_HVCURVETO 31
1889 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1891 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1892 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1893 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1894 #define CS_SEAC CS_1BYTE_MAX + 6
1895 #define CS_SBW CS_1BYTE_MAX + 7
1896 #define CS_DIV CS_1BYTE_MAX + 12
1897 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1898 #define CS_POP CS_1BYTE_MAX + 17
1899 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1900 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1901 #define CS_MAX CS_2BYTE_MAX
1903 @ @<Types...@>=
1904 typedef unsigned char byte;
1905 typedef struct {
1906 byte nargs; /* number of arguments */
1907 boolean bottom; /* take arguments from bottom of stack? */
1908 boolean clear; /* clear stack? */
1909 boolean valid;
1910 } cc_entry; /* CharString Command */
1911 typedef struct {
1912 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1913 byte *data;
1914 unsigned short len; /* length of the whole string */
1915 unsigned short cslen; /* length of the encoded part of the string */
1916 boolean is_used;
1917 boolean valid;
1918 } cs_entry;
1921 @d t1_c1 52845
1922 @d t1_c2 22719
1924 @<Glob...@>=
1925 unsigned short t1_dr, t1_er;
1926 unsigned short t1_cslen;
1927 short t1_lenIV;
1929 @ @<Types...@>=
1930 typedef char t1_line_entry;
1931 typedef char t1_buf_entry;
1933 @ @<Glob...@>=
1934 t1_line_entry *t1_line_ptr, *t1_line_array;
1935 size_t t1_line_limit;
1936 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1937 size_t t1_buf_limit;
1938 int cs_start;
1939 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1940 char *cs_dict_start, *cs_dict_end;
1941 int cs_count, cs_size, cs_size_pos;
1942 cs_entry *subr_tab;
1943 char *subr_array_start, *subr_array_end;
1944 int subr_max, subr_size, subr_size_pos;
1946 @ @<Set initial...@>=
1947 mp->ps->t1_line_array = NULL;
1948 mp->ps->t1_buf_array = NULL;
1951 This list contains the begin/end tokens commonly used in the
1952 /Subrs array of a Type 1 font.
1954 @<Static variables in the outer block@>=
1955 static const char *cs_token_pairs_list[][2] = {
1956 {" RD", "NP"},
1957 {" -|", "|"},
1958 {" RD", "noaccess put"},
1959 {" -|", "noaccess put"},
1960 {NULL, NULL}
1963 @ @<Glob...@>=
1964 const char **cs_token_pair;
1965 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1966 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1967 int t1_block_length;
1968 int last_hexbyte;
1969 void *t1_file;
1970 int hexline_length;
1973 @d HEXLINE_WIDTH 64
1975 @<Set initial ...@>=
1976 mp->ps->hexline_length = 0;
1979 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1980 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1981 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1982 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1983 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1984 @d t1_subrs() t1_prefix("/Subrs")
1985 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1986 @d t1_cleartomark() t1_prefix("cleartomark")
1989 static void end_hexline (MP mp) {
1990 if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
1991 wps_cr;
1992 mp->ps->hexline_length = 0;
1995 static void t1_check_pfa (MP mp) {
1996 const int c = t1_getchar (mp);
1997 mp->ps->t1_pfa = (c != 128) ? true : false;
1998 t1_ungetchar ();
2000 static int t1_getbyte (MP mp)
2002 int c = t1_getchar (mp);
2003 if (mp->ps->t1_pfa)
2004 return c;
2005 if (mp->ps->t1_block_length == 0) {
2006 if (c != 128)
2007 mp_fatal_error (mp, "invalid marker");
2008 c = t1_getchar (mp);
2009 if (c == 3) {
2010 while (!t1_eof ())
2011 (void)t1_getchar (mp);
2012 return EOF;
2014 mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
2015 mp->ps->t1_block_length |= (int)(((unsigned)t1_getchar (mp) & 0xff) << 8);
2016 mp->ps->t1_block_length |= (int)(((unsigned)t1_getchar (mp) & 0xff) << 16);
2017 mp->ps->t1_block_length |= (int)(((unsigned)t1_getchar (mp) & 0xff) << 24);
2018 c = t1_getchar (mp);
2020 mp->ps->t1_block_length--;
2021 return c;
2023 static int hexval (int c) {
2024 if (c >= 'A' && c <= 'F')
2025 return c - 'A' + 10;
2026 else if (c >= 'a' && c <= 'f')
2027 return c - 'a' + 10;
2028 else if (c >= '0' && c <= '9')
2029 return c - '0';
2030 else
2031 return -1;
2033 static byte edecrypt (MP mp, byte cipher) {
2034 byte plain;
2035 if (mp->ps->t1_pfa) {
2036 while (cipher == 10 || cipher == 13)
2037 cipher = (byte)t1_getbyte (mp);
2038 mp->ps->last_hexbyte = cipher = (byte)(((byte)hexval (cipher) << 4) +
2039 hexval (t1_getbyte (mp)));
2041 plain = (byte)(cipher ^ (mp->ps->t1_dr >> 8));
2042 mp->ps->t1_dr = (unsigned short)((cipher + mp->ps->t1_dr) * t1_c1 + t1_c2);
2043 return plain;
2045 static byte cdecrypt (byte cipher, unsigned short *cr)
2047 const byte plain = (byte)(cipher ^ (*cr >> 8));
2048 *cr = (unsigned short)((cipher + *cr) * t1_c1 + t1_c2);
2049 return plain;
2051 static byte eencrypt (MP mp, byte plain)
2053 const byte cipher = (byte)(plain ^ (mp->ps->t1_er >> 8));
2054 mp->ps->t1_er = (unsigned short)((cipher + mp->ps->t1_er) * t1_c1 + t1_c2);
2055 return cipher;
2058 static byte cencrypt (byte plain, unsigned short *cr)
2060 const byte cipher = (byte)(plain ^ (*cr >> 8));
2061 *cr = (unsigned short)((cipher + *cr) * t1_c1 + t1_c2);
2062 return cipher;
2065 static char *eol (char *s) {
2066 char *p = strend (s);
2067 if (p!=NULL && p - s > 1 && p[-1] != 10) {
2068 *p++ = 10;
2069 *p = 0;
2071 return p;
2073 static float t1_scan_num (MP mp, char *p, char **r)
2075 float f;
2076 char s[128];
2077 skip (p, ' ');
2078 if (sscanf (p, "%g", &f) != 1) {
2079 remove_eol (p, mp->ps->t1_line_array);
2080 mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
2081 mp_fatal_error(mp,s);
2083 if (r != NULL) {
2084 for (; mp_isdigit (*p) || *p == '.' ||
2085 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
2086 *r = p;
2088 return f;
2091 static boolean str_suffix (const char *begin_buf, const char *end_buf,
2092 const char *s)
2094 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
2095 if (*s1 == 10)
2096 s1--;
2097 while (s1 >= begin_buf && s2 >= s) {
2098 if (*s1-- != *s2--)
2099 return false;
2101 return s2 < s;
2106 @d alloc_array(T, n, s) do {
2107 size_t nn = (size_t)n;
2108 if (mp->ps->T##_array == NULL) {
2109 mp->ps->T##_limit = s;
2110 if (nn > mp->ps->T##_limit)
2111 mp->ps->T##_limit = nn;
2112 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
2113 mp->ps->T##_ptr = mp->ps->T##_array;
2115 else if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array) + nn > mp->ps->T##_limit) {
2116 size_t last_ptr_index;
2117 last_ptr_index = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array);
2118 mp->ps->T##_limit *= 2;
2119 mp->ps->T##_limit += s;
2120 if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array) + nn > mp->ps->T##_limit)
2121 mp->ps->T##_limit = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array) + nn;
2122 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
2123 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
2125 } while (0)
2128 static void t1_getline (MP mp) {
2129 int c, l, eexec_scan;
2130 char *p;
2131 static const char eexec_str[] = "currentfile eexec";
2132 static int eexec_len = 17; /* |strlen(eexec_str)| */
2133 RESTART:
2134 if (t1_eof ())
2135 mp_fatal_error (mp,"unexpected end of file");
2136 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2137 alloc_array (t1_line, 1, T1_BUF_SIZE);
2138 mp->ps->t1_cslen = 0;
2139 eexec_scan = 0;
2140 c = t1_getbyte (mp);
2141 if (c == EOF)
2142 goto EXIT;
2143 while (!t1_eof ()) {
2144 if (mp->ps->t1_in_eexec == 1)
2145 c = edecrypt (mp,(byte)c);
2146 alloc_array (t1_line, 1, T1_BUF_SIZE);
2147 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2148 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
2149 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
2150 eexec_scan++;
2151 else
2152 eexec_scan = -1;
2154 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2155 break;
2156 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
2157 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2158 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2159 p = mp->ps->t1_line_ptr - 5;
2160 while (*p != ' ')
2161 p--;
2162 l = (int)t1_scan_num (mp, p + 1, 0);
2163 mp->ps->t1_cslen = (unsigned short)l;
2164 mp->ps->cs_start = (int)(mp->ps->t1_line_ptr - mp->ps->t1_line_array);
2165 /* |mp->ps->cs_start| is an index now */
2166 alloc_array (t1_line, l, T1_BUF_SIZE);
2167 while (l-- > 0) {
2168 *mp->ps->t1_line_ptr = (t1_line_entry)edecrypt (mp,(byte)t1_getbyte (mp));
2169 mp->ps->t1_line_ptr++;
2172 c = t1_getbyte (mp);
2174 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
2175 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2176 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2177 goto RESTART;
2178 if (eexec_scan == eexec_len)
2179 mp->ps->t1_in_eexec = 1;
2180 EXIT:
2181 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2182 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2183 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2186 static void t1_putline (MP mp)
2188 char ss[256];
2189 int ss_cur = 0;
2190 static const char *hexdigits = "0123456789ABCDEF";
2191 char *p = mp->ps->t1_line_array;
2192 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2193 return;
2194 if (mp->ps->t1_eexec_encrypt) {
2195 while (p < mp->ps->t1_line_ptr) {
2196 byte b = eencrypt (mp,(byte)*p++);
2197 if (ss_cur>=253) {
2198 ss[ss_cur] = '\0';
2199 (mp->write_ascii_file)(mp,mp->output_file,(char *)ss);
2200 ss_cur = 0;
2202 ss[ss_cur++] = hexdigits[b / 16];
2203 ss[ss_cur++] = hexdigits[b % 16];
2204 mp->ps->hexline_length += 2;
2205 if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
2206 ss[ss_cur++] = '\n';
2207 mp->ps->hexline_length = 0;
2210 } else {
2211 while (p < mp->ps->t1_line_ptr) {
2212 if (ss_cur>=255) {
2213 ss[ss_cur] = '\0';
2214 (mp->write_ascii_file)(mp,mp->output_file,(char *)ss);
2215 ss_cur = 0;
2217 ss[ss_cur++] = (char)(*p++);
2220 ss[ss_cur] = '\0';
2221 (mp->write_ascii_file)(mp,mp->output_file,(char *)ss);
2224 static void t1_puts (MP mp, const char *s)
2226 if (s != mp->ps->t1_line_array)
2227 strcpy (mp->ps->t1_line_array, s);
2228 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2229 t1_putline (mp);
2232 static void t1_init_params (MP mp, const char *open_name_prefix,
2233 char *cur_file_name) {
2234 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2235 t1_log (open_name_prefix);
2236 t1_log (cur_file_name);
2238 mp->ps->t1_lenIV = 4;
2239 mp->ps->t1_dr = 55665;
2240 mp->ps->t1_er = 55665;
2241 mp->ps->t1_in_eexec = 0;
2242 mp->ps->t1_cs = false;
2243 mp->ps->t1_scan = true;
2244 mp->ps->t1_synthetic = false;
2245 mp->ps->t1_eexec_encrypt = false;
2246 mp->ps->t1_block_length = 0;
2247 t1_check_pfa (mp);
2249 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2250 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2251 t1_log (close_name_suffix);
2253 t1_close ();
2256 static void t1_check_block_len (MP mp, boolean decrypt) {
2257 int l, c;
2258 char s[128];
2259 if (mp->ps->t1_block_length == 0)
2260 return;
2261 c = t1_getbyte (mp);
2262 if (decrypt)
2263 c = edecrypt (mp,(byte)c);
2264 l = mp->ps->t1_block_length;
2265 if (!(l == 0 && (c == 10 || c == 13))) {
2266 mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2267 mp_warn(mp,s);
2268 while (l-- > 0)
2269 (void)t1_getbyte (mp);
2272 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2273 int i;
2274 if (!mp->ps->t1_pfa)
2275 t1_check_block_len (mp, false);
2276 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2277 (void)edecrypt (mp, (byte)t1_getbyte (mp));
2278 *mp->ps->t1_line_ptr++ = 0;
2280 mp->ps->t1_eexec_encrypt = true;
2281 if (!mp->ps->read_encoding_only)
2282 if (is_included (fm_cur))
2283 t1_putline (mp); /* to put the first four bytes */
2285 static void t1_stop_eexec (MP mp) {
2286 int c;
2287 end_last_eexec_line ();
2288 if (!mp->ps->t1_pfa)
2289 t1_check_block_len (mp,true);
2290 else {
2291 c = edecrypt (mp, (byte)t1_getbyte (mp));
2292 if (!(c == 10 || c == 13)) {
2293 if (mp->ps->last_hexbyte == 0)
2294 t1_puts (mp,"00");
2295 else
2296 mp_warn (mp,"unexpected data after eexec");
2299 mp->ps->t1_cs = false;
2300 mp->ps->t1_in_eexec = 2;
2302 static void t1_modify_fm (MP mp) {
2303 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2306 static void t1_modify_italic (MP mp) {
2307 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2310 @ @<Types...@>=
2311 typedef struct {
2312 const char *pdfname;
2313 const char *t1name;
2314 float value;
2315 boolean valid;
2316 } key_entry;
2319 @d FONT_KEYS_NUM 11
2321 @<Declarations@>=
2322 static key_entry font_keys[FONT_KEYS_NUM] = {
2323 {"Ascent", "Ascender", 0, false},
2324 {"CapHeight", "CapHeight", 0, false},
2325 {"Descent", "Descender", 0, false},
2326 {"FontName", "FontName", 0, false},
2327 {"ItalicAngle", "ItalicAngle", 0, false},
2328 {"StemV", "StdVW", 0, false},
2329 {"XHeight", "XHeight", 0, false},
2330 {"FontBBox", "FontBBox", 0, false},
2331 {"", "", 0, false},
2332 {"", "", 0, false},
2333 {"", "", 0, false}
2338 @d ASCENT_CODE 0
2339 @d CAPHEIGHT_CODE 1
2340 @d DESCENT_CODE 2
2341 @d FONTNAME_CODE 3
2342 @d ITALIC_ANGLE_CODE 4
2343 @d STEMV_CODE 5
2344 @d XHEIGHT_CODE 6
2345 @d FONTBBOX1_CODE 7
2346 @d FONTBBOX2_CODE 8
2347 @d FONTBBOX3_CODE 9
2348 @d FONTBBOX4_CODE 10
2349 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2352 static void t1_scan_keys (MP mp, font_number tex_font,fm_entry *fm_cur) {
2353 int i, k;
2354 char *p, *r;
2355 key_entry *key;
2356 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2357 if (t1_prefix ("/FontMatrix")) {
2358 t1_modify_fm (mp);
2359 return;
2361 if (t1_prefix ("/ItalicAngle")) {
2362 t1_modify_italic (mp);
2363 return;
2366 if (t1_prefix ("/FontType")) {
2367 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2368 if ((i = (int)t1_scan_num (mp,p, 0)) != 1) {
2369 char s[128];
2370 mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2371 mp_fatal_error(mp,s);
2373 return;
2375 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2376 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2377 break;
2378 if (key - font_keys == MAX_KEY_CODE)
2379 return;
2380 key->valid = true;
2381 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2382 skip (p, ' ');
2383 if ((k = (int)(key - font_keys)) == FONTNAME_CODE) {
2384 if (*p != '/') {
2385 char s[128];
2386 remove_eol (p, mp->ps->t1_line_array);
2387 mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2388 mp_fatal_error(mp,s);
2390 r = ++p; /* skip the slash */
2391 if (is_included (fm_cur)) {
2392 /* save the fontname */
2393 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2394 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2395 mp->ps->fontname_buf[i]=0;
2397 if(is_subsetted (fm_cur)) {
2398 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2399 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2400 else
2401 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2403 alloc_array (t1_line, (size_t)(r-mp->ps->t1_line_array)+6+1+strlen(mp->ps->fontname_buf)+1,
2404 T1_BUF_SIZE);
2405 strncpy (r, fm_cur->subset_tag , 6);
2406 *(r+6) = '-';
2407 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2408 mp->ps->t1_line_ptr = eol (r);
2409 } else {
2410 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2411 /*|*q = 0;|*/
2412 mp->ps->t1_line_ptr = eol (r);
2415 return;
2417 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2418 && (*p == '[' || *p == '{'))
2419 p++;
2420 if (k == FONTBBOX1_CODE) {
2421 for (i = 0; i < 4; i++) {
2422 key[i].value = t1_scan_num (mp, p, &r);
2423 p = r;
2425 return;
2427 key->value = t1_scan_num (mp, p, 0);
2429 static void t1_scan_param (MP mp, font_number tex_font,fm_entry *fm_cur)
2431 static const char *lenIV = "/lenIV";
2432 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2433 return;
2434 if (t1_prefix (lenIV)) {
2435 mp->ps->t1_lenIV = (short int)t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2436 return;
2438 t1_scan_keys (mp, tex_font,fm_cur);
2440 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2441 if (glyph_names[b] != notdef)
2442 mp_xfree (glyph_names[b]);
2443 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2445 static void t1_builtin_enc (MP mp) {
2446 int i, a, b, c, counter = 0;
2447 char *r, *p;
2449 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2451 if (t1_suffix ("def")) { /* predefined encoding */
2452 (void)sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2453 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2454 for (i = 0; i < 256; i++) {
2455 if (mp->ps->t1_builtin_glyph_names[i] != notdef)
2456 mp_xfree(mp->ps->t1_builtin_glyph_names[i]);
2457 mp->ps->t1_builtin_glyph_names[i] =
2458 mp_xstrdup (mp,standard_glyph_names[i]);
2460 mp->ps->t1_encoding = ENC_STANDARD;
2461 } else {
2462 char s[128];
2463 mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2464 mp->ps->t1_buf_array);
2465 mp_fatal_error(mp,s);
2467 return;
2468 } else
2469 mp->ps->t1_encoding = ENC_BUILTIN;
2471 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2472 * not a predefined encoding
2474 * We have two possible forms of Encoding vector. The first case is
2476 * /Encoding [/a /b /c...] readonly def
2478 * and the second case can look like
2480 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2481 * dup 0 /x put
2482 * dup 1 /y put
2483 * ...
2484 * readonly def
2486 for (i = 0; i < 256; i++) {
2487 if (mp->ps->t1_builtin_glyph_names[i] != notdef) {
2488 mp_xfree(mp->ps->t1_builtin_glyph_names[i]);
2489 mp->ps->t1_builtin_glyph_names[i] = mp_xstrdup(mp, notdef);
2492 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2493 r = strchr (mp->ps->t1_line_array, '[') + 1;
2494 skip (r, ' ');
2495 for (;;) {
2496 while (*r == '/') {
2497 for (p = mp->ps->t1_buf_array, r++;
2498 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2499 *p++ = *r++);
2500 *p = 0;
2501 skip (r, ' ');
2502 if (counter > 255) {
2503 mp_fatal_error
2504 (mp, "encoding vector contains more than 256 names");
2506 if (strcmp (mp->ps->t1_buf_array, notdef) != 0) {
2507 if (mp->ps->t1_builtin_glyph_names[counter] != notdef)
2508 mp_xfree(mp->ps->t1_builtin_glyph_names[counter]);
2509 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2511 counter++;
2513 if (*r != 10 && *r != '%') {
2514 if (str_prefix (r, "] def")
2515 || str_prefix (r, "] readonly def"))
2516 break;
2517 else {
2518 char s[128];
2519 remove_eol (r, mp->ps->t1_line_array);
2520 mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2521 mp->ps->t1_line_array);
2522 mp_fatal_error(mp,s);
2525 t1_getline (mp);
2526 r = mp->ps->t1_line_array;
2528 } else { /* the second case */
2529 p = strchr (mp->ps->t1_line_array, 10);
2530 for (;p!=NULL;) {
2531 if (*p == 10) {
2532 t1_getline (mp);
2533 p = mp->ps->t1_line_array;
2536 check for `dup <index> <glyph> put'
2538 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2539 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2540 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0) {
2541 if (mp->ps->t1_builtin_glyph_names[i] != notdef)
2542 mp_xfree(mp->ps->t1_builtin_glyph_names[i]);
2543 mp->ps->t1_builtin_glyph_names[i] =
2544 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2546 p = strstr (p, " put") + strlen (" put");
2547 skip (p, ' ');
2550 check for `dup dup <to> exch <from> get put'
2552 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2553 && valid_code (a) && valid_code (b)) {
2554 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2555 p = strstr (p, " get put") + strlen (" get put");
2556 skip (p, ' ');
2559 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2561 else if (sscanf
2562 (p, "dup dup %i %i getinterval %i exch putinterval",
2563 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2564 && valid_code (c)) {
2565 for (i = 0; i < c; i++)
2566 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2567 p = strstr (p, " putinterval") + strlen (" putinterval");
2568 skip (p, ' ');
2571 check for `def' or `readonly def'
2573 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2574 && strcmp (p, "def\n") == 0)
2575 return;
2577 skip an unrecognizable word
2579 else {
2580 while (*p != ' ' && *p != 10)
2581 p++;
2582 skip (p, ' ');
2588 static void t1_check_end (MP mp) {
2589 if (t1_eof ())
2590 return;
2591 t1_getline (mp);
2592 if (t1_prefix ("{restore}"))
2593 t1_putline (mp);
2596 @ @<Set initial values...@>=
2598 int i;
2599 for (i = 0; i < 256; i++) {
2600 mp->ps->t1_builtin_glyph_names[i] = strdup(notdef);
2601 assert(mp->ps->t1_builtin_glyph_names[i]);
2605 @ @<Types...@>=
2606 typedef struct {
2607 char *ff_name; /* base name of font file */
2608 char *ff_path; /* full path to font file */
2609 } ff_entry;
2611 @ @c
2612 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2613 ff_entry *ff;
2614 ff = check_ff_exist (mp, fm_cur);
2615 mp->ps->t1_file = NULL;
2616 if (ff->ff_path != NULL) {
2617 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font);
2619 if (mp->ps->t1_file == NULL) {
2620 char err [256];
2621 mp_snprintf(err, 255, "cannot open Type 1 font file %s for reading", ff->ff_path);
2622 mp_warn (mp,err);
2623 return false;
2625 t1_init_params (mp,open_name_prefix,fm_cur->ff_name);
2626 mp->ps->fontfile_found = true;
2627 return true;
2630 static void t1_scan_only (MP mp, font_number tex_font, fm_entry *fm_cur) {
2631 do {
2632 t1_getline (mp);
2633 t1_scan_param (mp,tex_font, fm_cur);
2635 while (mp->ps->t1_in_eexec == 0);
2636 t1_start_eexec (mp,fm_cur);
2637 do {
2638 t1_getline (mp);
2639 t1_scan_param (mp,tex_font, fm_cur);
2641 while (!(t1_charstrings () || t1_subrs ()));
2644 static void t1_include (MP mp, font_number tex_font, fm_entry *fm_cur) {
2645 do {
2646 t1_getline (mp);
2647 t1_scan_param (mp,tex_font, fm_cur);
2648 t1_putline (mp);
2650 while (mp->ps->t1_in_eexec == 0);
2651 t1_start_eexec (mp,fm_cur);
2652 do {
2653 t1_getline (mp);
2654 t1_scan_param (mp,tex_font, fm_cur);
2655 t1_putline (mp);
2657 while (!(t1_charstrings () || t1_subrs ()));
2658 mp->ps->t1_cs = true;
2659 do {
2660 t1_getline (mp);
2661 t1_putline (mp);
2663 while (!t1_end_eexec ());
2664 t1_stop_eexec (mp);
2665 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2666 do {
2667 t1_getline (mp);
2668 t1_putline (mp);
2670 while (!t1_cleartomark ());
2671 t1_check_end (mp); /* write "{restore}if" if found */
2676 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2677 char s[128];
2678 mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2679 mp_fatal_error(mp,s);
2683 static const char **check_cs_token_pair (MP mp) {
2684 const char **p = (const char **) cs_token_pairs_list;
2685 for (; p[0] != NULL; ++p)
2686 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2687 return p;
2688 return NULL;
2691 static void cs_store (MP mp, boolean is_subr) {
2692 char *p;
2693 cs_entry *ptr;
2694 int subr;
2695 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2696 *mp->ps->t1_buf_ptr++ = *p++);
2697 *mp->ps->t1_buf_ptr = 0;
2698 if (is_subr) {
2699 subr = (int)t1_scan_num (mp, p + 1, 0);
2700 check_subr (subr);
2701 ptr = mp->ps->subr_tab + subr;
2702 } else {
2703 ptr = mp->ps->cs_ptr++;
2704 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2705 char s[128];
2706 mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2707 mp_fatal_error(mp,s);
2709 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2711 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2712 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2713 (size_t) (mp->ps->t1_cslen + 4));
2714 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2715 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2716 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2717 *mp->ps->t1_buf_ptr++ = 10;
2718 if (is_subr && mp->ps->cs_token_pair == NULL)
2719 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2720 ptr->len = (unsigned short)(mp->ps->t1_buf_ptr - mp->ps->t1_buf_array);
2721 ptr->cslen = mp->ps->t1_cslen;
2722 ptr->data = mp_xmalloc (mp, (size_t)ptr->len , sizeof (byte));
2723 memcpy (ptr->data, mp->ps->t1_buf_array, (size_t)ptr->len);
2724 ptr->valid = true;
2727 #define store_subr(mp) cs_store(mp,true)
2728 #define store_cs(mp) cs_store(mp,false)
2730 #define CC_STACK_SIZE 24
2732 static double cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2733 static cc_entry cc_tab[CS_MAX];
2734 static boolean is_cc_init = false;
2737 #define cc_pop(N) \
2738 if (stack_ptr - cc_stack < (N)) \
2739 stack_error(N); \
2740 stack_ptr -= N
2742 #define stack_error(N) { \
2743 char s[256]; \
2744 mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2745 (int) N, (int)(stack_ptr - cc_stack)); \
2746 mp_warn(mp,s); \
2747 goto cs_error; \
2751 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2753 #define cc_push(V) *stack_ptr++ = (double)(V)
2754 #define cc_clear() stack_ptr = cc_stack
2756 #define set_cc(N, B, A, C) \
2757 cc_tab[N].nargs = A; \
2758 cc_tab[N].bottom = B; \
2759 cc_tab[N].clear = C; \
2760 cc_tab[N].valid = true
2762 static void cc_init (void) {
2763 int i;
2764 if (is_cc_init)
2765 return;
2766 for (i = 0; i < CS_MAX; i++)
2767 cc_tab[i].valid = false;
2768 set_cc (CS_HSTEM, true, 2, true);
2769 set_cc (CS_VSTEM, true, 2, true);
2770 set_cc (CS_VMOVETO, true, 1, true);
2771 set_cc (CS_RLINETO, true, 2, true);
2772 set_cc (CS_HLINETO, true, 1, true);
2773 set_cc (CS_VLINETO, true, 1, true);
2774 set_cc (CS_RRCURVETO, true, 6, true);
2775 set_cc (CS_CLOSEPATH, false, 0, true);
2776 set_cc (CS_CALLSUBR, false, 1, false);
2777 set_cc (CS_RETURN, false, 0, false);
2779 |set_cc(CS_ESCAPE, false, 0, false);|
2781 set_cc (CS_HSBW, true, 2, true);
2782 set_cc (CS_ENDCHAR, false, 0, true);
2783 set_cc (CS_RMOVETO, true, 2, true);
2784 set_cc (CS_HMOVETO, true, 1, true);
2785 set_cc (CS_VHCURVETO, true, 4, true);
2786 set_cc (CS_HVCURVETO, true, 4, true);
2787 set_cc (CS_DOTSECTION, false, 0, true);
2788 set_cc (CS_VSTEM3, true, 6, true);
2789 set_cc (CS_HSTEM3, true, 6, true);
2790 set_cc (CS_SEAC, true, 5, true);
2791 set_cc (CS_SBW, true, 4, true);
2792 set_cc (CS_DIV, false, 2, false);
2793 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2794 set_cc (CS_POP, false, 0, false);
2795 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2796 is_cc_init = true;
2801 @d cs_getchar(mp) cdecrypt(*data++, &cr)
2803 @d mark_subr(mp,n) cs_mark(mp,0, n)
2804 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2805 @d SMALL_BUF_SIZE 256
2808 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2809 char buf[SMALL_BUF_SIZE];
2810 char s[300];
2811 va_list args;
2812 va_start (args, fmt);
2813 @= /*@@-bufferoverflowhigh@@*/ @>
2814 (void)vsprintf (buf, fmt, args);
2815 @= /*@@=bufferoverflowhigh@@*/ @>
2816 va_end (args);
2817 if (cs_name == NULL) {
2818 mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2819 } else {
2820 mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2822 mp_warn(mp,s);
2825 static void cs_mark (MP mp, const char *cs_name, int subr)
2827 byte *data;
2828 int i, b, cs_len;
2829 integer a, a1, a2;
2830 unsigned short cr;
2831 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2832 OtherSubrs[3] */
2833 cs_entry *ptr;
2834 cc_entry *cc;
2835 if (cs_name == NULL) {
2836 check_subr (subr);
2837 ptr = mp->ps->subr_tab + subr;
2838 if (!ptr->valid)
2839 return;
2840 } else {
2841 if (mp->ps->cs_notdef != NULL &&
2842 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2843 ptr = mp->ps->cs_notdef;
2844 else {
2845 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2846 if (strcmp (ptr->glyph_name, cs_name) == 0)
2847 break;
2848 if (ptr == mp->ps->cs_ptr) {
2849 char s[128];
2850 mp_snprintf (s,128,"glyph `%s' undefined", cs_name);
2851 mp_warn(mp,s);
2852 return;
2854 if (ptr->glyph_name == notdef)
2855 mp->ps->cs_notdef = ptr;
2858 /* only marked CharString entries and invalid entries can be skipped;
2859 valid marked subrs must be parsed to keep the stack in sync */
2860 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2861 return;
2862 ptr->is_used = true;
2863 cr = 4330;
2864 cs_len = (int)ptr->cslen;
2865 data = ptr->data + 4;
2866 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2867 (void)cs_getchar (mp);
2868 while (cs_len > 0) {
2869 --cs_len;
2870 b = cs_getchar (mp);
2871 if (b >= 32) {
2872 if (b <= 246)
2873 a = b - 139;
2874 else if (b <= 250) {
2875 --cs_len;
2876 a = (int)((unsigned)(b - 247) << 8) + 108 + cs_getchar (mp);
2877 } else if (b <= 254) {
2878 --cs_len;
2879 a = -(int)((unsigned)(b - 251) << 8) - 108 - cs_getchar (mp);
2880 } else {
2881 cs_len -= 4;
2882 a = (cs_getchar (mp) & 0xff) << 24;
2883 a |= (cs_getchar (mp) & 0xff) << 16;
2884 a |= (cs_getchar (mp) & 0xff) << 8;
2885 a |= (cs_getchar (mp) & 0xff) << 0;
2886 if (sizeof (integer) > 4 && (a & 0x80000000))
2887 a |= ~0x7FFFFFFF;
2889 cc_push (a);
2890 } else {
2891 if (b == CS_ESCAPE) {
2892 b = cs_getchar (mp) + CS_1BYTE_MAX;
2893 cs_len--;
2895 if (b >= CS_MAX) {
2896 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2897 (int) b);
2898 goto cs_error;
2900 cc = cc_tab + b;
2901 if (!cc->valid) {
2902 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2903 goto cs_error;
2905 if (cc->bottom) {
2906 if (stack_ptr - cc_stack < cc->nargs)
2907 cs_warn (mp,cs_name, subr,
2908 "less arguments on stack (%i) than required (%i)",
2909 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2910 else if (stack_ptr - cc_stack > cc->nargs)
2911 cs_warn (mp,cs_name, subr,
2912 "more arguments on stack (%i) than required (%i)",
2913 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2915 switch (cc - cc_tab) {
2916 case CS_CALLSUBR:
2917 a1 = (integer)cc_get (-1);
2918 cc_pop (1);
2919 mark_subr (mp,a1);
2920 if (!mp->ps->subr_tab[a1].valid) {
2921 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2922 goto cs_error;
2924 break;
2925 case CS_DIV:
2926 cc_pop (2);
2927 cc_push (0);
2928 break;
2929 case CS_CALLOTHERSUBR:
2930 a1 = (integer)cc_get (-1);
2931 if (a1 == 3)
2932 lastargOtherSubr3 = (integer)cc_get (-3);
2933 a1 = (integer)cc_get (-2) + 2;
2934 cc_pop (a1);
2935 break;
2936 case CS_POP:
2937 cc_push (lastargOtherSubr3);
2938 /* the only case when we care about the value being pushed onto
2939 stack is when POP follows CALLOTHERSUBR (changing hints by
2940 OtherSubrs[3])
2942 break;
2943 case CS_SEAC:
2944 a1 = (integer)cc_get (3);
2945 a2 = (integer)cc_get (4);
2946 cc_clear ();
2947 mark_cs (mp,standard_glyph_names[a1]);
2948 mark_cs (mp,standard_glyph_names[a2]);
2949 break;
2950 default:
2951 if (cc->clear)
2952 cc_clear ();
2956 return;
2957 cs_error: /* an error occured during parsing */
2958 cc_clear ();
2959 ptr->valid = false;
2960 ptr->is_used = false;
2963 static void t1_subset_ascii_part (MP mp, font_number tex_font, fm_entry *fm_cur)
2965 int i, j;
2966 t1_getline (mp);
2967 while (!t1_prefix ("/Encoding")) {
2968 t1_scan_param (mp,tex_font, fm_cur);
2969 /* Patch the initial font directory cacheing mechanism found in some
2970 * pfb fonts.
2972 * Even though the T1 spec does not explicitly state that 'FontDirectory'
2973 * should appear at the start of a line, luckily this is standard practise.
2975 if (t1_prefix ("FontDirectory")) {
2976 char *endloc, *p;
2977 char new_line[T1_BUF_SIZE] = {0};
2978 p = mp->ps->t1_line_array;
2979 while ((endloc = strstr(p,fm_cur->ps_name)) != NULL) {
2980 int n = (endloc-mp->ps->t1_line_array) + strlen(fm_cur->subset_tag) + 2 + strlen(fm_cur->ps_name);
2981 if (n >= T1_BUF_SIZE) {
2982 mp_fatal_error (mp, "t1_subset_ascii_part: buffer overrun detected.");
2984 strncat(new_line,p,(endloc-p));
2985 strcat(new_line,fm_cur->subset_tag);
2986 strcat(new_line,"-");
2987 strcat(new_line,fm_cur->ps_name);
2988 p = endloc + strlen(fm_cur->ps_name);
2990 if (strlen(new_line) + strlen(p) + 1 >= T1_BUF_SIZE ) {
2991 mp_fatal_error (mp, "t1_subset_ascii_part: buffer overrun detected.");
2993 strcat(new_line, p);
2994 strcpy(mp->ps->t1_line_array,new_line);
2995 mp->ps->t1_line_ptr = mp->ps->t1_line_array + strlen(mp->ps->t1_line_array);
2996 t1_putline (mp);
2997 } else {
2998 t1_putline (mp);
3000 t1_getline (mp);
3002 t1_builtin_enc (mp);
3003 if (is_reencoded (fm_cur))
3004 mp->ps->t1_glyph_names = external_enc ();
3005 else
3006 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
3007 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
3008 t1_puts (mp,"/Encoding StandardEncoding def\n");
3009 else {
3010 t1_puts
3011 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
3012 for (i = 0, j = 0; i < 256; i++) {
3013 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef &&
3014 strcmp(mp->ps->t1_glyph_names[i],notdef) != 0) {
3015 j++;
3016 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3017 "dup %i /%s put\n", (int) t1_char (i),
3018 mp->ps->t1_glyph_names[i]);
3019 t1_puts(mp,mp->ps->t1_line_array);
3022 /* We didn't mark anything for the Encoding array. */
3023 /* We add "dup 0 /.notdef put" for compatibility */
3024 /* with Acrobat 5.0. */
3025 if (j == 0)
3026 t1_puts (mp,"dup 0 /.notdef put\n");
3027 t1_puts (mp,"readonly def\n");
3029 do {
3030 t1_getline (mp);
3031 t1_scan_param (mp,tex_font, fm_cur);
3032 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
3033 t1_putline (mp);
3035 while (mp->ps->t1_in_eexec == 0);
3038 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
3039 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
3041 static void cs_init (MP mp) {
3042 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
3043 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
3044 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
3045 mp->ps->cs_token_pair = NULL;
3046 mp->ps->subr_tab = NULL;
3047 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
3048 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
3051 static void init_cs_entry ( cs_entry * cs) {
3052 cs->data = NULL;
3053 cs->glyph_name = NULL;
3054 cs->len = 0;
3055 cs->cslen = 0;
3056 cs->is_used = false;
3057 cs->valid = false;
3060 static void t1_mark_glyphs (MP mp, font_number tex_font);
3062 static void t1_read_subrs (MP mp, font_number tex_font, fm_entry *fm_cur, int read_only)
3064 int i, s;
3065 cs_entry *ptr;
3066 t1_getline (mp);
3067 while (!(t1_charstrings () || t1_subrs ())) {
3068 t1_scan_param (mp,tex_font, fm_cur);
3069 if (!read_only)
3070 t1_putline (mp);
3071 t1_getline (mp);
3073 FOUND:
3074 mp->ps->t1_cs = true;
3075 mp->ps->t1_scan = false;
3076 if (!t1_subrs ())
3077 return;
3078 mp->ps->subr_size_pos = (int)(strlen ("/Subrs") + 1);
3079 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
3080 mp->ps->subr_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
3081 if (mp->ps->subr_size == 0) {
3082 while (!t1_charstrings ())
3083 t1_getline (mp);
3084 return;
3086 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
3087 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,(size_t)mp->ps->subr_size, sizeof (cs_entry));
3088 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3089 init_cs_entry (ptr);
3090 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3091 t1_getline (mp);
3092 while (mp->ps->t1_cslen) {
3093 store_subr (mp);
3094 t1_getline (mp);
3096 /* mark the first four entries without parsing */
3097 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
3098 mp->ps->subr_tab[i].is_used = true;
3099 /* the end of the Subrs array might have more than one line so we need to
3100 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
3101 the Subrs array followed by the CharStrings dict immediately (synthetic
3102 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
3103 we will treat the font as synthetic and ignore everything until next
3104 Subrs is found
3106 #define POST_SUBRS_SCAN 5
3107 s = 0;
3108 *mp->ps->t1_buf_array = 0;
3109 for (i = 0; i < POST_SUBRS_SCAN; i++) {
3110 if (t1_charstrings ())
3111 break;
3112 s += (int)(mp->ps->t1_line_ptr - mp->ps->t1_line_array);
3113 alloc_array (t1_buf, s, T1_BUF_SIZE);
3114 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
3115 t1_getline (mp);
3117 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
3118 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
3119 suppose synthetic font */
3120 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3121 if (ptr->valid)
3122 mp_xfree (ptr->data);
3123 mp_xfree (mp->ps->subr_tab);
3124 mp_xfree (mp->ps->subr_array_start);
3125 mp_xfree (mp->ps->subr_array_end);
3126 cs_init (mp);
3127 mp->ps->t1_cs = false;
3128 mp->ps->t1_synthetic = true;
3129 while (!(t1_charstrings () || t1_subrs ()))
3130 t1_getline (mp);
3131 goto FOUND;
3135 @ @c
3136 static void t1_flush_cs (MP mp, boolean is_subr)
3138 char *p;
3139 byte *r, *return_cs = NULL;
3140 cs_entry *tab, *end_tab, *ptr;
3141 char *start_line, *line_end;
3142 int count, size_pos;
3143 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
3144 if (is_subr) {
3145 start_line = mp->ps->subr_array_start;
3146 line_end = mp->ps->subr_array_end;
3147 size_pos = mp->ps->subr_size_pos;
3148 tab = mp->ps->subr_tab;
3149 count = mp->ps->subr_max + 1;
3150 end_tab = mp->ps->subr_tab + count;
3151 } else {
3152 start_line = mp->ps->cs_dict_start;
3153 line_end = mp->ps->cs_dict_end;
3154 size_pos = mp->ps->cs_size_pos;
3155 tab = mp->ps->cs_tab;
3156 end_tab = mp->ps->cs_ptr;
3157 count = mp->ps->cs_count;
3159 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
3160 for (p = start_line; p - start_line < size_pos;)
3161 *mp->ps->t1_line_ptr++ = *p++;
3162 while (mp_isdigit (*p))
3163 p++;
3164 mp_snprintf (mp->ps->t1_line_ptr, (int)mp->ps->t1_line_limit, "%u", (unsigned)count);
3165 strcat (mp->ps->t1_line_ptr, p);
3166 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3167 t1_putline (mp);
3169 /* create |return_cs| to replace unsused subr's */
3170 if (is_subr) {
3171 cr = 4330;
3172 cs_len = 0;
3173 return_cs = mp_xmalloc (mp, (size_t)(mp->ps->t1_lenIV + 1) , sizeof(byte));
3174 if ( mp->ps->t1_lenIV >= 0) {
3175 for (cs_len = 0, r = return_cs;
3176 cs_len<(unsigned short)mp->ps->t1_lenIV; cs_len++, r++)
3177 *r = cencrypt (0x00, &cr);
3178 *r = cencrypt (CS_RETURN, &cr);
3179 } else {
3180 *return_cs = CS_RETURN;
3182 cs_len++;
3185 for (ptr = tab; ptr < end_tab; ptr++) {
3186 if (ptr->is_used) {
3187 if (is_subr)
3188 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3189 "dup %i %u", (int) (ptr - tab), ptr->cslen);
3190 else
3191 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3192 "/%s %u", ptr->glyph_name, ptr->cslen);
3193 p = strend (mp->ps->t1_line_array);
3194 memcpy (p, ptr->data, (size_t)ptr->len);
3195 mp->ps->t1_line_ptr = p + ptr->len;
3196 t1_putline (mp);
3197 } else {
3198 /* replace unsused subr's by |return_cs| */
3199 if (is_subr) {
3200 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3201 "dup %i %u%s ", (int) (ptr - tab),
3202 cs_len, mp->ps->cs_token_pair[0]);
3203 p = strend (mp->ps->t1_line_array);
3204 memcpy (p, return_cs, (size_t)cs_len);
3205 mp->ps->t1_line_ptr = p + cs_len;
3206 t1_putline (mp);
3207 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3208 " %s", mp->ps->cs_token_pair[1]);
3209 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3210 t1_putline (mp);
3213 mp_xfree (ptr->data);
3214 if (ptr->glyph_name != notdef)
3215 mp_xfree (ptr->glyph_name);
3217 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "%s", line_end);
3218 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3219 t1_putline (mp);
3220 if (is_subr)
3221 mp_xfree (return_cs);
3222 mp_xfree (tab);
3223 mp_xfree (start_line);
3224 mp_xfree (line_end);
3225 if (is_subr) {
3226 mp->ps->subr_array_start = NULL;
3227 mp->ps->subr_array_end = NULL;
3228 mp->ps->subr_tab = NULL;
3229 } else {
3230 mp->ps->cs_dict_start = NULL;
3231 mp->ps->cs_dict_end = NULL;
3232 mp->ps->cs_tab = NULL;
3236 static void t1_mark_glyphs (MP mp, font_number tex_font)
3238 int i;
3239 char *charset = extra_charset ();
3240 char *g, *s, *r;
3241 cs_entry *ptr;
3242 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
3243 if (mp->ps->cs_tab != NULL)
3244 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3245 if (ptr->valid)
3246 ptr->is_used = true;
3247 if (mp->ps->subr_tab != NULL) {
3248 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3249 if (ptr->valid)
3250 ptr->is_used = true;
3251 mp->ps->subr_max = mp->ps->subr_size - 1;
3253 return;
3255 mark_cs (mp,notdef);
3256 for (i = 0; i < 256; i++)
3257 if (is_used_char (i)) {
3258 if (mp->ps->t1_glyph_names[i] == notdef ||
3259 strcmp(mp->ps->t1_glyph_names[i],notdef)==0) {
3260 char S[128];
3261 mp_snprintf(S,128, "character %i is mapped to %s", i, notdef);
3262 mp_warn(mp,S);
3263 } else
3264 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3266 if (charset == NULL)
3267 goto SET_SUBR_MAX;
3268 g = s = charset + 1; /* skip the first '/' */
3269 r = strend (g);
3270 while (g < r) {
3271 while (*s != '/' && s < r)
3272 s++;
3273 *s = 0; /* terminate g by rewriting '/' to 0 */
3274 mark_cs (mp,g);
3275 g = s + 1;
3277 SET_SUBR_MAX:
3278 if (mp->ps->subr_tab != NULL)
3279 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3280 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3281 ptr++)
3282 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3283 mp->ps->subr_max = (int)(ptr - mp->ps->subr_tab);
3286 static void t1_do_subset_charstrings (MP mp, font_number tex_font)
3288 cs_entry *ptr;
3289 mp->ps->cs_size_pos = (int)(
3290 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3291 - mp->ps->t1_line_array + 1);
3292 /* |cs_size_pos| points to the number indicating
3293 dict size after "/CharStrings" */
3294 mp->ps->cs_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3295 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,(size_t)mp->ps->cs_size, sizeof(cs_entry));
3296 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3297 init_cs_entry (ptr);
3298 mp->ps->cs_notdef = NULL;
3299 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3300 t1_getline (mp);
3301 while (mp->ps->t1_cslen) {
3302 store_cs (mp);
3303 t1_getline (mp);
3305 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3306 t1_mark_glyphs (mp,tex_font);
3309 static void t1_subset_charstrings (MP mp, font_number tex_font)
3311 cs_entry *ptr;
3312 t1_do_subset_charstrings (mp, tex_font);
3313 if (mp->ps->subr_tab != NULL) {
3314 if (mp->ps->cs_token_pair == NULL)
3315 mp_fatal_error
3316 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3317 t1_subr_flush (mp);
3319 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3320 if (ptr->is_used)
3321 mp->ps->cs_count++;
3322 t1_cs_flush (mp);
3325 static void t1_subset_end (MP mp)
3327 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3328 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3329 t1_getline (mp);
3330 t1_putline (mp);
3332 while (!t1_end_eexec ())
3333 t1_getline (mp); /* ignore the rest */
3334 t1_putline (mp); /* write "mark currentfile closefile" */
3335 } else
3336 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3337 t1_getline (mp);
3338 t1_putline (mp);
3340 t1_stop_eexec (mp);
3341 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3342 while (!t1_cleartomark ()) {
3343 t1_getline (mp);
3344 t1_putline (mp);
3346 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3347 t1_check_end (mp); /* write "{restore}if" if found */
3351 static int t1_updatefm (MP mp, font_number f, fm_entry *fm)
3353 char *s, *p;
3354 mp->ps->read_encoding_only = true;
3355 if (!t1_open_fontfile (mp,fm,NULL)) {
3356 return 0;
3358 t1_scan_only (mp,f, fm);
3359 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3360 p = s;
3361 while (*p != ' ' && *p != 0)
3362 p++;
3363 *p=0;
3364 mp_xfree(fm->ps_name);
3365 fm->ps_name = s;
3366 t1_close_font_file (mp,"");
3367 return 1;
3371 static void writet1 (MP mp, font_number tex_font, fm_entry *fm_cur) {
3372 unsigned save_selector = mp->selector;
3373 mp_normalize_selector(mp);
3374 mp->ps->read_encoding_only = false;
3375 if (!is_included (fm_cur)) { /* scan parameters from font file */
3376 if (!t1_open_fontfile (mp,fm_cur,"{"))
3377 return;
3378 t1_scan_only (mp,tex_font, fm_cur);
3379 t1_close_font_file (mp,"}");
3380 return;
3382 if (!is_subsetted (fm_cur)) { /* include entire font */
3383 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3384 return;
3385 t1_include (mp,tex_font,fm_cur);
3386 t1_close_font_file (mp,">>");
3387 return;
3389 /* partial downloading */
3390 if (!t1_open_fontfile (mp,fm_cur,"<"))
3391 return;
3392 t1_subset_ascii_part (mp,tex_font,fm_cur);
3393 t1_start_eexec (mp,fm_cur);
3394 cc_init ();
3395 cs_init (mp);
3396 t1_read_subrs (mp,tex_font, fm_cur, false);
3397 t1_subset_charstrings (mp,tex_font);
3398 t1_subset_end (mp);
3399 t1_close_font_file (mp,">");
3400 mp->selector = save_selector;
3403 @ @<Declarations@>=
3404 static void t1_free (MP mp);
3406 @ @c
3407 static void t1_free (MP mp) {
3408 int k;
3410 mp_xfree (mp->ps->subr_array_start);
3411 mp_xfree (mp->ps->subr_array_end);
3412 mp_xfree (mp->ps->cs_dict_start);
3413 mp_xfree (mp->ps->cs_dict_end);
3414 cs_init(mp);
3416 mp_xfree (mp->ps->t1_line_array);
3417 mp_xfree (mp->ps->char_array);
3418 mp->ps->char_array=NULL;
3420 mp->ps->t1_line_array = mp->ps->t1_line_ptr = NULL;
3421 mp->ps->t1_line_limit = 0;
3422 mp_xfree (mp->ps->t1_buf_array);
3423 mp->ps->t1_buf_array = mp->ps->t1_buf_ptr = NULL;
3424 mp->ps->t1_buf_limit = 0;
3426 for (k=0;k<=255;k++) {
3427 if (mp->ps->t1_builtin_glyph_names[k] != notdef)
3428 mp_xfree(mp->ps->t1_builtin_glyph_names[k]);
3429 mp->ps->t1_builtin_glyph_names[k] = notdef;
3433 @* Embedding Charstrings.
3435 The SVG backend uses some routines that use an ascii representation of
3436 a type1 font. First, here is the type associated with it:
3438 @<Types ...@>=
3439 typedef struct mp_ps_font {
3440 int font_num; /* just to put something in */
3441 char **t1_glyph_names;
3442 cs_entry *cs_tab;
3443 cs_entry *cs_ptr;
3444 cs_entry *subr_tab;
3445 int subr_size;
3446 int t1_lenIV;
3447 int slant;
3448 int extend;
3449 @<Variables for the charstring parser@>
3450 } mp_ps_font;
3452 @ The parser creates a structure and fills it.
3456 mp_ps_font *mp_ps_font_parse (MP mp, int tex_font) {
3457 mp_ps_font *f;
3458 fm_entry *fm_cur;
3459 char msg[128];
3460 (void)mp_has_fm_entry (mp, (font_number)tex_font, &fm_cur);
3461 if (fm_cur == NULL) {
3462 mp_snprintf(msg,128,"fontmap entry for `%s' not found", mp->font_name[tex_font]);
3463 mp_warn(mp,msg);
3464 return NULL;
3466 if (is_truetype(fm_cur) ||
3467 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL) ||
3468 (!is_included(fm_cur))) {
3469 mp_snprintf(msg,128,"font `%s' cannot be embedded", mp->font_name[tex_font]);
3470 mp_warn(mp,msg);
3471 return NULL;
3473 if (!t1_open_fontfile (mp,fm_cur,"<")) { /* message handled there */
3474 return NULL;
3476 f = mp_xmalloc(mp, 1, sizeof(struct mp_ps_font));
3477 f->font_num = tex_font;
3478 f->t1_glyph_names = NULL;
3479 f->cs_tab = NULL;
3480 f->cs_ptr = NULL;
3481 f->subr_tab = NULL;
3482 f->orig_x = f->orig_y = 0.0;
3483 f->slant = (int)fm_cur->slant;
3484 f->extend = (int)fm_cur->extend;
3485 t1_getline (mp);
3486 while (!t1_prefix ("/Encoding")) {
3487 t1_scan_param (mp, (font_number)tex_font, fm_cur);
3488 t1_getline (mp);
3490 t1_builtin_enc (mp);
3491 if (is_reencoded (fm_cur)) {
3492 mp_read_enc (mp, fm_cur->encoding);;
3493 f->t1_glyph_names = external_enc ();
3494 } else {
3495 f->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
3497 do {
3498 t1_getline (mp);
3499 t1_scan_param (mp, (font_number)tex_font, fm_cur);
3500 } while (mp->ps->t1_in_eexec == 0);
3502 /* t1_start_eexec (mp,fm_cur); */
3503 cc_init ();
3504 cs_init (mp);
3505 /* the boolean is needed to make sure that |t1_read_subrs|
3506 doesn't output stuff */
3507 t1_read_subrs (mp, (font_number)tex_font, fm_cur, true);
3508 mp->ps->t1_synthetic = true ;
3509 t1_do_subset_charstrings (mp, (font_number)tex_font);
3510 f->cs_tab = mp->ps->cs_tab;
3511 mp->ps->cs_tab = NULL;
3512 f->cs_ptr = mp->ps->cs_ptr;
3513 mp->ps->cs_ptr = NULL;
3514 f->subr_tab = mp->ps->subr_tab;
3515 mp->ps->subr_tab = NULL;
3516 f->subr_size = mp->ps->subr_size;
3517 mp->ps->subr_size = mp->ps->subr_size_pos = 0;
3518 f->t1_lenIV = mp->ps->t1_lenIV;
3519 t1_close_font_file (mp,">");
3520 return f;
3523 @ @<Exported function headers@>=
3524 mp_ps_font *mp_ps_font_parse (MP mp, int tex_font);
3526 @ Freeing the structure
3529 void mp_ps_font_free (MP mp, mp_ps_font *f) {
3530 cs_entry *ptr;
3531 for (ptr = f->cs_tab; ptr < f->cs_ptr; ptr++) {
3532 if (ptr->glyph_name != notdef)
3533 mp_xfree (ptr->glyph_name);
3534 mp_xfree(ptr->data);
3536 mp_xfree(f->cs_tab);
3537 f->cs_tab = NULL;
3538 for (ptr = f->subr_tab; ptr - f->subr_tab < f->subr_size; ptr++) {
3539 if (ptr->glyph_name != notdef)
3540 mp_xfree (ptr->glyph_name);
3541 mp_xfree(ptr->data);
3543 mp_xfree(f->subr_tab);
3544 f->subr_tab = NULL;
3545 t1_free(mp);
3546 mp_xfree(f);
3549 @ @<Exported function headers@>=
3550 void mp_ps_font_free (MP mp, mp_ps_font *f);
3553 @ Parsing Charstrings.
3555 @<Variables for the charstring parser@>=
3556 double flex_hint_data[14]; /* store temp. coordinates of flex hints */
3557 unsigned int flex_hint_index ; /* index for flex_hint_data */
3558 boolean ignore_flex_hint; /* skip hint for flex */
3559 double cur_x, cur_y; /* current point */
3560 double orig_x, orig_y; /* origin (for seac) */
3561 mp_edge_object *h; /* the whole picture */
3562 mp_graphic_object *p; /* the current subpath in the picture */
3563 mp_gr_knot pp; /* the last known knot in the subpath */
3566 @ @c
3567 mp_edge_object *mp_ps_do_font_charstring (MP mp, mp_ps_font *f, char *nam) {
3568 mp_edge_object *h = NULL;
3569 f->h = NULL; f->p = NULL; f->pp = NULL; f->ignore_flex_hint=0; f->flex_hint_index=0 ;/* just in case */
3570 f->cur_x = f->cur_y = 0.0;
3571 f->orig_x = f->orig_y = 0.0;
3572 if (nam==NULL) {
3573 mp_warn(mp,"nonexistant glyph requested");
3574 return h;
3576 if (cs_parse(mp,f,nam, 0)) {
3577 h = f->h;
3578 } else {
3579 char err[256];
3580 mp_snprintf(err,255,"Glyph interpreter failed (missing glyph '%s'?)", nam);
3581 mp_warn(mp,err);
3582 if (f->h != NULL) {
3583 finish_subpath(mp, f);
3584 mp_gr_toss_objects(f->h);
3587 f->h = NULL; f->p = NULL; f->pp = NULL;
3588 return h;
3591 mp_edge_object *mp_ps_font_charstring (MP mp, mp_ps_font *f, int c) {
3592 char *s = NULL;
3593 if (f != NULL && f->t1_glyph_names != NULL && c>=0 && c<256)
3594 s = f->t1_glyph_names[c];
3595 return mp_ps_do_font_charstring(mp,f,s);
3600 @ @<Exported function headers@>=
3601 mp_edge_object *mp_ps_font_charstring (MP mp, mp_ps_font *f, int c);
3602 mp_edge_object *mp_ps_do_font_charstring (MP mp, mp_ps_font *f, char *n);
3606 @<Declarations@>=
3607 boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr);
3611 static void start_subpath(MP mp, mp_ps_font *f, double dx, double dy)
3613 assert(f->pp == NULL);
3614 assert(f->p == NULL);
3615 f->pp = mp_xmalloc(mp, 1, sizeof (struct mp_gr_knot_data));
3616 f->pp->data.types.left_type = mp_explicit;
3617 f->pp->data.types.right_type = mp_explicit;
3618 f->pp->x_coord = (f->cur_x + dx);
3619 f->pp->y_coord = (f->cur_y + dy);
3620 f->pp->left_x = f->pp->right_x = f->pp->x_coord;
3621 f->pp->left_y = f->pp->right_y = f->pp->y_coord;
3622 f->pp->next = NULL;
3623 f->cur_x += dx;
3624 f->cur_y += dy;
3625 f->p = mp_new_graphic_object(mp,mp_fill_code);
3626 gr_path_p((mp_fill_object *)f->p) = f->pp;
3629 static void add_line_segment(MP mp, mp_ps_font *f, double dx, double dy)
3631 mp_gr_knot n;
3632 assert(f->pp != NULL);
3633 n = mp_xmalloc(mp,1, sizeof (struct mp_gr_knot_data));
3634 n->data.types.left_type = mp_explicit;
3635 n->data.types.right_type = mp_explicit;
3636 n->next = gr_path_p((mp_fill_object *)f->p); /* loop */
3637 n->x_coord = (f->cur_x + dx);
3638 n->y_coord = (f->cur_y + dy);
3639 n->right_x = n->x_coord;
3640 n->right_y = n->y_coord;
3641 n->left_x = n->x_coord;
3642 n->left_y = n->y_coord;
3643 f->pp->next = n;
3644 f->pp = n;
3645 f->cur_x += dx;
3646 f->cur_y += dy;
3649 static void add_curve_segment(MP mp, mp_ps_font *f, double dx1, double dy1, double dx2,
3650 double dy2, double dx3, double dy3)
3652 mp_gr_knot n;
3653 n = mp_xmalloc(mp, 1, sizeof (struct mp_gr_knot_data));
3654 n->data.types.left_type = mp_explicit;
3655 n->data.types.right_type = mp_explicit;
3656 n->next = gr_path_p((mp_fill_object *)f->p); /* loop */
3657 n->x_coord = (f->cur_x + dx1 + dx2 + dx3);
3658 n->y_coord = (f->cur_y + dy1 + dy2 + dy3);
3659 n->right_x = n->x_coord;
3660 n->right_y = n->y_coord;
3661 n->left_x = (f->cur_x + dx1 + dx2);
3662 n->left_y = (f->cur_y + dy1 + dy2);
3663 f->pp->right_x = (f->cur_x + dx1);
3664 f->pp->right_y = (f->cur_y + dy1);
3665 f->pp->next = n;
3666 f->pp = n;
3667 f->cur_x += dx1 + dx2 + dx3;
3668 f->cur_y += dy1 + dy2 + dy3;
3671 static void finish_subpath(MP mp, mp_ps_font *f)
3673 if (f->p != NULL) {
3674 if (f->h->body == NULL) {
3675 f->h->body = f->p;
3676 } else {
3677 mp_graphic_object *q = f->h->body;
3678 while (gr_link(q) != NULL)
3679 q = gr_link(q);
3680 q->next = f->p;
3683 if (f->p!=NULL) {
3684 mp_gr_knot r, rr;
3685 assert(f->pp != NULL);
3686 r = gr_path_p((mp_fill_object *)f->p);
3687 rr = r;
3688 if (r) {
3689 if (r == f->pp ) {
3690 r->next = r;
3691 } else if ( r->x_coord == f->pp->x_coord && r->y_coord == f->pp->y_coord ) {
3692 while (rr->next != f->pp)
3693 rr = rr->next;
3694 rr->next = r;
3695 r->left_x = f->pp->left_x;
3696 r->left_y = f->pp->left_y;
3697 mp_xfree(f->pp);
3701 f->p = NULL;
3702 f->pp = NULL;
3706 @d cs_no_debug(A) cs_do_debug(mp,f,A,#A)
3707 @d cs_debug(A)
3709 @<Declarations@>=
3710 void cs_do_debug (MP mp, mp_ps_font *f, int i, char *s);
3711 static void finish_subpath(MP mp, mp_ps_font *f);
3712 static void add_curve_segment(MP mp, mp_ps_font *f, double dx1, double dy1, double dx2,
3713 double dy2, double dx3, double dy3);
3714 static void add_line_segment(MP mp, mp_ps_font *f, double dx, double dy);
3715 static void start_subpath(MP mp, mp_ps_font *f, double dx, double dy);
3717 @ @c
3718 void cs_do_debug (MP mp, mp_ps_font *f, int i, char *s) {
3719 int n = cc_tab[i].nargs;
3720 (void)mp; /* for -Wall */
3721 (void)f; /* for -Wall */
3722 while (n>0) {
3723 fprintf (stdout,"%d ", (int)cc_get((-n)));
3724 n--;
3726 fprintf (stdout,"%s\n", s);
3729 boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr)
3731 byte *data;
3732 int i, b, cs_len;
3733 integer a, a1, a2;
3734 unsigned short cr;
3735 static integer lastargOtherSubr3 = 3;
3737 cs_entry *ptr;
3738 cc_entry *cc;
3740 if (cs_name == NULL) {
3741 ptr = f->subr_tab + subr;
3742 } else {
3743 i = 0;
3744 for (ptr = f->cs_tab; ptr < f->cs_ptr; ptr++, i++) {
3745 if (strcmp (ptr->glyph_name, cs_name) == 0)
3746 break;
3748 ptr = f->cs_tab+i; /* this is the right charstring */
3750 if (ptr==f->cs_ptr)
3751 return false;
3752 data = ptr->data + 4;
3753 cr = 4330;
3754 cs_len = (int)ptr->cslen;
3755 for (i = 0; i < f->t1_lenIV; i++, cs_len--)
3756 (void)cs_getchar (mp);
3758 while (cs_len > 0) {
3759 --cs_len;
3760 b = cs_getchar(mp);
3761 if (b >= 32) {
3762 if (b <= 246)
3763 a = b - 139;
3764 else if (b <= 250) {
3765 --cs_len;
3766 a = (int)((unsigned)(b - 247) << 8) + 108 + cs_getchar (mp);
3767 } else if (b <= 254) {
3768 --cs_len;
3769 a = -(int)((unsigned)(b - 251) << 8) - 108 - cs_getchar (mp);
3770 } else {
3771 cs_len -= 4;
3772 a = (cs_getchar (mp) & 0xff) << 24;
3773 a |= (cs_getchar (mp) & 0xff) << 16;
3774 a |= (cs_getchar (mp) & 0xff) << 8;
3775 a |= (cs_getchar (mp) & 0xff) << 0;
3776 if (sizeof (integer) > 4 && (a & 0x80000000))
3777 a |= ~0x7FFFFFFF;
3779 cc_push (a);
3780 } else {
3781 if (b == CS_ESCAPE) {
3782 b = cs_getchar (mp) + CS_1BYTE_MAX;
3783 cs_len--;
3785 if (b >= CS_MAX) {
3786 cs_warn (mp,cs_name, subr, "command value out of range: %i",
3787 (int) b);
3788 goto cs_error;
3790 cc = cc_tab + b;
3791 if (!cc->valid) {
3792 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
3793 goto cs_error;
3795 if (cc->bottom) {
3796 if (stack_ptr - cc_stack < cc->nargs)
3797 cs_warn (mp,cs_name, subr,
3798 "less arguments on stack (%i) than required (%i)",
3799 (int) (stack_ptr - cc_stack), (int) cc->nargs);
3800 else if (stack_ptr - cc_stack > cc->nargs)
3801 cs_warn (mp,cs_name, subr,
3802 "more arguments on stack (%i) than required (%i)",
3803 (int) (stack_ptr - cc_stack), (int) cc->nargs);
3805 switch (cc - cc_tab) {
3806 case CS_CLOSEPATH: /* - CLOSEPATH |- */
3807 cs_debug(CS_CLOSEPATH);
3808 finish_subpath(mp, f);
3809 cc_clear ();
3810 break;
3811 case CS_HLINETO: /* |- dx HLINETO |- */
3812 cs_debug(CS_HLINETO);
3813 add_line_segment(mp,f,cc_get(-1),0);
3814 cc_clear ();
3815 break;
3816 case CS_HVCURVETO: /* |- dx1 dx2 dy2 dy3 HVCURVETO |- */
3817 cs_debug(CS_HVCURVETO);
3818 add_curve_segment(mp,f,cc_get(-4),0,cc_get(-3),cc_get(-2),0,cc_get(-1));
3819 cc_clear ();
3820 break;
3821 case CS_RLINETO: /* |- dx dy RLINETO |- */
3822 cs_debug(CS_RLINETO);
3823 add_line_segment(mp,f,cc_get(-2),cc_get(-1));
3824 cc_clear ();
3825 break;
3826 case CS_RRCURVETO: /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
3827 cs_debug(CS_RRCURVETO);
3828 add_curve_segment(mp,f,cc_get(-6),cc_get(-5),cc_get(-4),cc_get(-3),cc_get(-2),cc_get(-1));
3829 cc_clear ();
3830 break;
3831 case CS_VHCURVETO: /* |- dy1 dx2 dy2 dx3 VHCURVETO |- */
3832 cs_debug(CS_VHCURVETO);
3833 add_curve_segment(mp,f,0, cc_get(-4),cc_get(-3),cc_get(-2),cc_get(-1),0);
3834 cc_clear ();
3835 break;
3836 case CS_VLINETO: /* |- dy VLINETO |- */
3837 cs_debug(CS_VLINETO);
3838 add_line_segment(mp,f,0,cc_get(-1));
3839 cc_clear ();
3840 break;
3841 case CS_HMOVETO: /* |- dx HMOVETO |- */
3842 cs_debug(CS_HMOVETO);
3843 /* treating in-line moves as 'line segments' work better than attempting
3844 to split the path up in two separate sections, at least for now. */
3845 if (f->pp == NULL) { /* this is the first */
3846 start_subpath(mp,f,cc_get(-1),0);
3847 } else {
3848 add_line_segment(mp,f,cc_get(-1),0);
3850 cc_clear ();
3851 break;
3852 case CS_RMOVETO: /* |- dx dy RMOVETO |- */
3853 cs_debug(CS_RMOVETO);
3854 if (f->ignore_flex_hint == 1) {
3855 f->flex_hint_data[f->flex_hint_index++] = cc_get(-2);
3856 f->flex_hint_data[f->flex_hint_index++] = cc_get(-1);
3857 } else {
3858 if (f->pp == NULL) { /* this is the first */
3859 start_subpath(mp,f,cc_get(-2),cc_get(-1));
3860 } else {
3861 add_line_segment(mp,f,cc_get(-2),cc_get(-1));
3864 cc_clear ();
3865 break;
3866 case CS_VMOVETO: /* |- dy VMOVETO |- */
3867 cs_debug(CS_VMOVETO);
3868 if (f->pp == NULL) { /* this is the first */
3869 start_subpath(mp,f,0,cc_get(-1));
3870 } else {
3871 add_line_segment(mp,f,0,cc_get(-1));
3873 cc_clear ();
3874 break;
3875 /* hinting commands */
3876 case CS_DOTSECTION: /* - DOTSECTION |- */
3877 cs_debug(CS_DOTSECTION);
3878 cc_clear ();
3879 break;
3880 case CS_HSTEM: /* |- y dy HSTEM |- */
3881 cs_debug(CS_HSTEM);
3882 cc_clear ();
3883 break;
3884 case CS_HSTEM3: /* |- y0 dy0 y1 dy1 y2 dy2 HSTEM3 |- */
3885 cs_debug(CS_HSTEM3);
3886 cc_clear ();
3887 break;
3888 case CS_VSTEM: /* |- x dx VSTEM |- */
3889 cs_debug(CS_VSTEM);
3890 cc_clear ();
3891 break;
3892 case CS_VSTEM3: /* |- x0 dx0 x1 dx1 x2 dx2 VSTEM3 |- */
3893 cs_debug(CS_VSTEM3);
3894 cc_clear ();
3895 break;
3896 /* start and close commands */
3897 case CS_SEAC: /* |- asb adx ady bchar achar SEAC |- */
3898 cs_debug(CS_SEAC);
3899 { double adx, ady, asb;
3900 asb = cc_get (0);
3901 adx = cc_get (1);
3902 ady = cc_get (2);
3903 a1 = (integer)cc_get (3);
3904 a2 = (integer)cc_get (4);
3905 cc_clear ();
3906 (void)cs_parse(mp,f,standard_glyph_names[a1],0); /* base */
3907 f->orig_x += (adx - asb);
3908 f->orig_y += ady;
3909 (void)cs_parse(mp,f,standard_glyph_names[a2],0);
3911 break;
3912 case CS_ENDCHAR: /* - ENDCHAR |- */
3913 cs_debug(CS_ENDCHAR);
3914 cc_clear ();
3915 return true;
3916 break;
3917 case CS_HSBW: /* |- sbx wx HSBW |- */
3918 cs_debug(CS_HSBW);
3919 if (!f->h) {
3920 f->h = mp_xmalloc(mp, 1,sizeof(mp_edge_object));
3921 f->h->body = NULL; f->h->next = NULL;
3922 f->h->parent = mp;
3923 f->h->filename = NULL;
3924 f->h->minx = f->h->miny = f->h->maxx = f->h->maxy = 0.0;
3926 f->cur_x = cc_get(-2) + f->orig_x;
3927 f->cur_y = 0.0 + f->orig_y;
3928 f->orig_x = f->cur_x;
3929 f->orig_y = f->cur_y;
3930 cc_clear ();
3931 break;
3932 case CS_SBW: /* |- sbx sby wx wy SBW |- */
3933 cs_debug(CS_SBW);
3934 if (!f->h) {
3935 f->h = mp_xmalloc(mp, 1,sizeof(mp_edge_object));
3936 f->h->body = NULL; f->h->next = NULL;
3937 f->h->parent = mp;
3938 f->h->filename = NULL;
3939 f->h->minx = f->h->miny = f->h->maxx = f->h->maxy = 0.0;
3941 f->cur_x = cc_get(-4) + f->orig_x;
3942 f->cur_y = cc_get(-3) + f->orig_y;
3943 f->orig_x = f->cur_x;
3944 f->orig_y = f->cur_y;
3945 cc_clear ();
3946 break;
3947 /* arithmetic */
3948 case CS_DIV: /* num1 num2 DIV quotient */
3949 cs_debug(CS_DIV);
3950 { double num,den,res;
3951 num = cc_get (-2);
3952 den = cc_get (-1);
3953 res = num/den;
3954 cc_pop (2);
3955 cc_push (res);
3956 break;
3958 /* subrs */
3959 case CS_CALLSUBR: /* subr CALLSUBR - */
3960 cs_debug(CS_CALLSUBR);
3961 a1 = (integer)cc_get (-1);
3962 if (a1==1)
3963 f->ignore_flex_hint = 1;
3964 if (a1==0) {
3965 /* double first_x,first_y,first_r_x,first_r_y; */
3966 /* double join_x,join_y,join_l_x,join_l_y,join_r_x,join_r_y; */
3967 /* double last_x,last_y,last_l_x,last_l_y; */
3968 /* /\* a := glyph "q" of "cmti12"; *\/ */
3969 /* first_x = 206.0; first_y = -194.0; */
3970 /* double ref_x,ref_y ; */
3971 /* ref_x = first_x+f->flex_hint_data[0]; */
3972 /* ref_y = first_y+f->flex_hint_data[1]; */
3973 /* printf("1:(%f, %f) 2:(%f,%f) 3:(%f,%f) 4:(%f,%f) 5:(%f,%f) 6:(%f,%f) 7:(%f,%f)\n", */
3974 /* f->flex_hint_data[0],f->flex_hint_data[1], */
3975 /* f->flex_hint_data[2],f->flex_hint_data[3], */
3976 /* f->flex_hint_data[4],f->flex_hint_data[5], */
3977 /* f->flex_hint_data[6],f->flex_hint_data[7], */
3978 /* f->flex_hint_data[8],f->flex_hint_data[9], */
3979 /* f->flex_hint_data[10],f->flex_hint_data[11], */
3980 /* f->flex_hint_data[12],f->flex_hint_data[13]); */
3981 /* printf("Reference=(%f,%f)\n",ref_x,ref_y); */
3982 /* first_r_x = ref_x + f->flex_hint_data[2]; first_r_y = ref_y + f->flex_hint_data[3]; */
3983 /* join_l_x = first_r_x + f->flex_hint_data[4]; join_l_y = first_r_y + f->flex_hint_data[5]; */
3984 /* join_x = join_l_x + f->flex_hint_data[6]; join_y = join_l_y + f->flex_hint_data[7]; */
3985 /* join_r_x = join_x + f->flex_hint_data[8]; join_r_y = join_y + f->flex_hint_data[9]; */
3986 /* last_l_x = join_r_x + f->flex_hint_data[10]; last_l_y = join_r_y + f->flex_hint_data[11]; */
3987 /* last_x = last_l_x + f->flex_hint_data[12]; last_y = last_l_y + f->flex_hint_data[13]; */
3988 /* printf("(%f,%f) .. (%f,%f) and (%f,%f) .. (%f,%f) .. (%f,%f) and (%f,%f) .. (%f,%f)\n", */
3989 /* first_x,first_y,first_r_x,first_r_y, join_l_x,join_l_y, join_x,join_y, join_r_x,join_r_y, */
3990 /* last_l_x,last_l_y,last_x,last_y); */
3992 f->ignore_flex_hint = 0;
3993 f->flex_hint_index = 0;
3994 add_curve_segment(mp,f,f->flex_hint_data[0]+ f->flex_hint_data[2],f->flex_hint_data[1]+ f->flex_hint_data[3],
3995 f->flex_hint_data[4],f->flex_hint_data[5],
3996 f->flex_hint_data[6],f->flex_hint_data[7]);
3997 add_curve_segment(mp,f,f->flex_hint_data[8],f->flex_hint_data[9],
3998 f->flex_hint_data[10],f->flex_hint_data[11],
3999 f->flex_hint_data[12],f->flex_hint_data[13]);
4001 cc_pop (1);
4002 (void)cs_parse(mp,f,NULL,a1);
4003 break;
4004 case CS_RETURN: /* - RETURN - */
4005 cs_debug(CS_RETURN);
4006 return true;
4007 break;
4008 case CS_CALLOTHERSUBR: /* arg1 ... argn n othersubr CALLOTHERSUBR - */
4009 cs_debug(CS_CALLOTHERSUBR);
4010 a1 = (integer)cc_get (-1);
4011 if (a1 == 3)
4012 lastargOtherSubr3 = (integer)cc_get (-3);
4013 a1 = (integer)cc_get(-2) + 2;
4014 cc_pop (a1);
4015 break;
4016 case CS_POP: /* - POP number */
4017 cc_push (lastargOtherSubr3);
4018 break;
4019 case CS_SETCURRENTPOINT: /* |- x y SETCURRENTPOINT |- */
4020 cs_debug(CS_SETCURRENTPOINT) ;
4021 /* totally ignoring setcurrentpoint actually works better for most fonts ? */
4022 cc_clear ();
4023 break;
4024 default:
4025 if (cc->clear)
4026 cc_clear ();
4030 return true;
4031 cs_error: /* an error occured during parsing */
4032 cc_clear ();
4033 ptr->valid = false;
4034 ptr->is_used = false;
4035 return false;
4038 @* \[44d] Embedding fonts.
4040 @ The |tfm_num| is officially of type |font_number|, but that
4041 type does not exist yet at this point in the output order.
4043 @<Types...@>=
4044 typedef struct {
4045 char *tfm_name; /* TFM file name */
4046 char *ps_name; /* PostScript name */
4047 integer flags; /* font flags */
4048 char *ff_name; /* font file name */
4049 char *subset_tag; /* pseudoUniqueTag for subsetted font */
4050 enc_entry *encoding; /* pointer to corresponding encoding */
4051 unsigned int tfm_num; /* number of the TFM refering this entry */
4052 unsigned short type; /* font type (T1/TTF/...) */
4053 short slant; /* SlantFont */
4054 short extend; /* ExtendFont */
4055 integer ff_objnum; /* FontFile object number */
4056 integer fn_objnum; /* FontName/BaseName object number */
4057 integer fd_objnum; /* FontDescriptor object number */
4058 char *charset; /* string containing used glyphs */
4059 boolean all_glyphs; /* embed all glyphs? */
4060 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
4061 short tfm_avail; /* flags whether a tfm is available */
4062 short pid; /* Pid for truetype fonts */
4063 short eid; /* Eid for truetype fonts */
4064 } fm_entry;
4068 @<Glob...@>=
4069 #define FONTNAME_BUF_SIZE 128
4070 boolean fontfile_found;
4071 boolean is_otf_font;
4072 char fontname_buf[FONTNAME_BUF_SIZE];
4075 @d F_INCLUDED 0x01
4076 @d F_SUBSETTED 0x02
4077 @d F_TRUETYPE 0x04
4078 @d F_BASEFONT 0x08
4080 @d set_included(fm) ((fm)->type |= F_INCLUDED)
4081 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
4082 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
4083 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
4085 @d is_included(fm) ((fm)->type & F_INCLUDED)
4086 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
4087 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
4088 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
4089 @d is_reencoded(fm) ((fm)->encoding != NULL)
4090 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
4091 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
4093 @d fm_slant(fm) (fm)->slant
4094 @d fm_extend(fm) (fm)->extend
4095 @d fm_fontfile(fm) (fm)->ff_name
4097 @<Declarations@>=
4098 static boolean mp_font_is_reencoded (MP mp, font_number f);
4099 static boolean mp_font_is_included (MP mp, font_number f);
4100 static boolean mp_font_is_subsetted (MP mp, font_number f);
4102 @ @c
4103 boolean mp_font_is_reencoded (MP mp, font_number f) {
4104 fm_entry *fm;
4105 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
4106 if (fm != NULL
4107 && (fm->ps_name != NULL)
4108 && is_reencoded (fm))
4109 return true;
4111 return false;
4113 boolean mp_font_is_included (MP mp, font_number f) {
4114 fm_entry *fm;
4115 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
4116 if (fm != NULL
4117 && (fm->ps_name != NULL && fm->ff_name != NULL)
4118 && is_included (fm))
4119 return true;
4121 return false;
4123 boolean mp_font_is_subsetted (MP mp, font_number f) {
4124 fm_entry *fm;
4125 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
4126 if (fm != NULL
4127 && (fm->ps_name != NULL && fm->ff_name != NULL)
4128 && is_included (fm) && is_subsetted (fm))
4129 return true;
4131 return false;
4134 @ @<Declarations@>=
4135 static char * mp_fm_encoding_name (MP mp, font_number f);
4136 static char * mp_fm_font_name (MP mp, font_number f);
4137 static char * mp_fm_font_subset_name (MP mp, font_number f);
4140 @c char * mp_fm_encoding_name (MP mp, font_number f) {
4141 enc_entry *e;
4142 fm_entry *fm;
4143 if (mp_has_fm_entry (mp, f, &fm)) {
4144 if (fm != NULL && (fm->ps_name != NULL)) {
4145 if (is_reencoded (fm)) {
4146 e = fm->encoding;
4147 if (e->enc_name!=NULL)
4148 return mp_xstrdup(mp,e->enc_name);
4149 } else {
4150 return NULL;
4155 char msg[256];
4156 mp_snprintf (msg, 256, "fontmap encoding problems for font %s", mp->font_name[f]);
4157 mp_error(mp, msg, NULL, true);
4159 return NULL;
4161 char * mp_fm_font_name (MP mp, font_number f) {
4162 fm_entry *fm;
4163 if (mp_has_fm_entry (mp, f,&fm)) {
4164 if (fm != NULL && (fm->ps_name != NULL)) {
4165 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
4166 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
4167 if (t1_updatefm(mp,f,fm)) {
4168 mp->font_ps_name_fixed[f] = true;
4169 } else {
4170 char msg[256];
4171 mp_snprintf (msg, 256, "font loading problems for font %s", mp->font_name[f]);
4172 mp_error(mp, msg, NULL, true);
4175 return mp_xstrdup(mp,fm->ps_name);
4179 char msg[256];
4180 mp_snprintf (msg, 256, "fontmap name problems for font %s", mp->font_name[f]);
4181 mp_error(mp, msg, NULL, true);
4183 return NULL;
4186 static char * mp_fm_font_subset_name (MP mp, font_number f) {
4187 fm_entry *fm;
4188 if (mp_has_fm_entry (mp, f, &fm)) {
4189 if (fm != NULL && (fm->ps_name != NULL)) {
4190 if (is_subsetted(fm)) {
4191 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
4192 mp_snprintf(s,(int)strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
4193 return s;
4194 } else {
4195 return mp_xstrdup(mp,fm->ps_name);
4200 char msg[256];
4201 mp_snprintf (msg, 256, "fontmap name problems for font %s", mp->font_name[f]);
4202 mp_error(mp, msg, NULL, true);
4204 return NULL;
4207 @ @<Declarations@>=
4208 static integer mp_fm_font_slant (MP mp, font_number f);
4209 static integer mp_fm_font_extend (MP mp, font_number f);
4212 @c static integer mp_fm_font_slant (MP mp, font_number f) {
4213 fm_entry *fm;
4214 if (mp_has_fm_entry (mp, f, &fm)) {
4215 if (fm != NULL && (fm->ps_name != NULL)) {
4216 return fm->slant;
4219 return 0;
4221 static integer mp_fm_font_extend (MP mp, font_number f) {
4222 fm_entry *fm;
4223 if (mp_has_fm_entry (mp, f, &fm)) {
4224 if (fm != NULL && (fm->ps_name != NULL)) {
4225 return fm->extend;
4228 return 0;
4231 @ @<Declarations@>=
4232 static boolean mp_do_ps_font (MP mp, font_number f);
4234 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
4235 fm_entry *fm_cur;
4236 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
4237 if (fm_cur == NULL)
4238 return true;
4239 if (is_truetype(fm_cur) ||
4240 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
4241 return false;
4243 if (is_included(fm_cur)) {
4244 mp_ps_print_nl(mp,"%%BeginResource: font ");
4245 if (is_subsetted(fm_cur)) {
4246 mp_ps_print(mp, fm_cur->subset_tag);
4247 mp_ps_print_char(mp,'-');
4249 mp_ps_print(mp, fm_cur->ps_name);
4250 mp_ps_print_ln(mp);
4251 writet1 (mp,f,fm_cur);
4252 mp_ps_print_nl(mp,"%%EndResource");
4253 mp_ps_print_ln(mp);
4255 return true;
4258 @ Included subset fonts do not need and encoding vector, make
4259 sure we skip that case.
4261 @<Declarations@>=
4262 static void mp_list_used_resources (MP mp, int prologues, int procset);
4264 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
4265 font_number f; /* fonts used in a text node or as loop counters */
4266 int ff; /* a loop counter */
4267 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
4268 boolean firstitem;
4269 if ( procset>0 )
4270 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
4271 else
4272 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
4273 ldf=null_font;
4274 firstitem=true;
4275 for (f=null_font+1;f<=mp->last_fnum;f++) {
4276 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
4277 for (ff=ldf;ff>=null_font;ff--) {
4278 if ( mp_has_font_size(mp,(font_number)ff) )
4279 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
4280 goto FOUND;
4282 if ( mp_font_is_subsetted(mp,f) )
4283 goto FOUND;
4284 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
4285 (size_t)mp->max_print_line )
4286 mp_ps_print_nl(mp, "%%+ encoding");
4287 if ( firstitem ) {
4288 firstitem=false;
4289 mp_ps_print_nl(mp, "%%+ encoding");
4291 mp_ps_print_char(mp, ' ');
4292 mp_ps_dsc_print(mp, "encoding", mp->font_enc_name[f]);
4293 ldf=(int)f;
4295 FOUND:
4298 ldf=null_font;
4299 firstitem=true;
4300 for (f=null_font+1;f<=mp->last_fnum;f++) {
4301 if ( mp_has_font_size(mp,f) ) {
4302 for (ff=ldf;ff>=null_font;ff--) {
4303 if ( mp_has_font_size(mp,(font_number)ff) )
4304 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
4305 goto FOUND2;
4307 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
4308 (size_t)mp->max_print_line )
4309 mp_ps_print_nl(mp, "%%+ font");
4310 if ( firstitem ) {
4311 firstitem=false;
4312 mp_ps_print_nl(mp, "%%+ font");
4314 mp_ps_print_char(mp, ' ');
4315 if ( (prologues==3)&& (mp_font_is_subsetted(mp,f)) ) {
4316 char *s = mp_fm_font_subset_name(mp,f);
4317 mp_ps_dsc_print(mp, "font", s);
4318 mp_xfree(s);
4319 } else {
4320 mp_ps_dsc_print(mp, "font", mp->font_ps_name[f]);
4322 ldf=(int)f;
4324 FOUND2:
4327 mp_ps_print_ln(mp);
4330 @ @<Declarations@>=
4331 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
4333 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
4334 font_number f; /* fonts used in a text node or as loop counters */
4335 int ff; /* a loop counter */
4336 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
4337 boolean firstitem;
4338 if ( procset>0 )
4339 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
4340 else
4341 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
4342 ldf=null_font;
4343 firstitem=true;
4344 for (f=null_font+1;f<=mp->last_fnum;f++) {
4345 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
4346 for (ff=ldf;ff>= null_font;ff++) {
4347 if ( mp_has_font_size(mp,(font_number)ff) )
4348 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
4349 goto FOUND;
4351 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
4352 goto FOUND;
4353 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(size_t)mp->max_print_line )
4354 mp_ps_print_nl(mp, "%%+ encoding");
4355 if ( firstitem ) {
4356 firstitem=false;
4357 mp_ps_print_nl(mp, "%%+ encoding");
4359 mp_ps_print_char(mp, ' ');
4360 mp_ps_dsc_print(mp, "encoding", mp->font_enc_name[f]);
4361 ldf=(int)f;
4363 FOUND:
4366 ldf=null_font;
4367 firstitem=true;
4368 if (prologues==3) {
4369 for (f=null_font+1;f<=mp->last_fnum;f++) {
4370 if ( mp_has_font_size(mp,f) ) {
4371 for (ff=ldf;ff>= null_font;ff--) {
4372 if ( mp_has_font_size(mp,(font_number)ff) )
4373 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
4374 goto FOUND2;
4376 if ( ! mp_font_is_included(mp,f) )
4377 goto FOUND2;
4378 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
4379 mp_ps_print_nl(mp, "%%+ font");
4380 if ( firstitem ) {
4381 firstitem=false;
4382 mp_ps_print_nl(mp, "%%+ font");
4384 mp_ps_print_char(mp, ' ');
4385 if ( mp_font_is_subsetted(mp,f) ) {
4386 char *s = mp_fm_font_subset_name(mp,f);
4387 mp_ps_dsc_print(mp, "font", s);
4388 mp_xfree(s);
4389 } else {
4390 mp_ps_dsc_print(mp, "font", mp->font_ps_name[f]);
4392 ldf=(int)f;
4394 FOUND2:
4397 mp_ps_print_ln(mp);
4401 @ @<Declarations...@>=
4402 static void mp_list_needed_resources (MP mp, int prologues);
4404 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
4405 font_number f; /* fonts used in a text node or as loop counters */
4406 int ff; /* a loop counter */
4407 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
4408 boolean firstitem;
4409 ldf=null_font;
4410 firstitem=true;
4411 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
4412 if ( mp_has_font_size(mp,f)) {
4413 for (ff=ldf;ff>=null_font;ff--) {
4414 if ( mp_has_font_size(mp,(font_number)ff) )
4415 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
4416 goto FOUND;
4418 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
4419 goto FOUND;
4420 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
4421 mp_ps_print_nl(mp, "%%+ font");
4422 if ( firstitem ) {
4423 firstitem=false;
4424 mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
4426 mp_ps_print_char(mp, ' ');
4427 mp_ps_dsc_print(mp, "font", mp->font_ps_name[f]);
4428 ldf=(int)f;
4430 FOUND:
4433 if ( ! firstitem ) {
4434 mp_ps_print_ln(mp);
4435 ldf=null_font;
4436 /* clang: never read: firstitem=true; */
4437 for (f=null_font+1;f<= mp->last_fnum;f++) {
4438 if ( mp_has_font_size(mp,f) ) {
4439 for (ff=ldf;ff>=null_font;ff-- ) {
4440 if ( mp_has_font_size(mp,(font_number)ff) )
4441 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
4442 goto FOUND2;
4444 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
4445 goto FOUND2;
4446 mp_ps_print(mp, "%%IncludeResource: font ");
4447 mp_ps_print(mp, mp->font_ps_name[f]);
4448 mp_ps_print_ln(mp);
4449 ldf=(int)f;
4451 FOUND2:
4457 @ @<Declarations@>=
4458 static void mp_write_font_definition (MP mp, font_number f, int prologues);
4462 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
4463 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
4465 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
4466 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
4467 (mp_fm_font_extend(mp,f)!=0)||
4468 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
4469 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
4470 if ( (mp_font_is_subsetted(mp,f))&&
4471 (mp_font_is_included(mp,f))&&(prologues==3)) {
4472 char *s = mp_fm_font_subset_name(mp,f);
4473 mp_ps_name_out(mp, s,true);
4474 mp_xfree(s);
4475 } else {
4476 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4478 mp_ps_print(mp, " fcp");
4479 mp_ps_print_ln(mp);
4480 if ( applied_reencoding(f) ) {
4481 mp_ps_print(mp, "/Encoding ");
4482 mp_ps_print(mp, mp->font_enc_name[f]);
4483 mp_ps_print(mp, " def ");
4485 if ( mp_fm_font_slant(mp,f)!=0 ) {
4486 mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
4487 mp_ps_print(mp, " SlantFont ");
4489 if ( mp_fm_font_extend(mp,f)!=0 ) {
4490 mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
4491 mp_ps_print(mp, " ExtendFont ");
4493 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
4494 mp_ps_print(mp, " 890 ScaleFont ");
4495 mp_ps_print(mp, " 277 SlantFont ");
4497 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
4498 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
4499 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
4500 mp_ps_print(mp, "/space[0 -278]def ");
4501 mp_ps_print(mp, "/a12[-904 -939]def ");
4502 mp_ps_print(mp, "end def ");
4504 mp_ps_print(mp, "currentdict end");
4505 mp_ps_print_ln(mp);
4506 mp_ps_print_defined_name(mp,f,prologues);
4507 mp_ps_print(mp, " exch definefont pop");
4508 mp_ps_print_ln(mp);
4512 @ @<Declarations@>=
4513 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
4516 @c static void mp_ps_print_defined_name(MP mp, font_number f, int prologues) {
4517 mp_ps_print(mp, " /");
4518 if ((mp_font_is_subsetted(mp,f))&&
4519 (mp_font_is_included(mp,f))&&(prologues==3)) {
4520 char *s = mp_fm_font_subset_name(mp,f);
4521 mp_ps_print(mp, s);
4522 mp_xfree(s);
4523 } else {
4524 mp_ps_print(mp, mp->font_ps_name[f]);
4526 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 )
4527 mp_ps_print(mp, "-Slanted");
4528 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 )
4529 mp_ps_print(mp, "-Reverse");
4530 if ( applied_reencoding(f) ) {
4531 mp_ps_print(mp, "-");
4532 mp_ps_print(mp, mp->font_enc_name[f]);
4534 if ( mp_fm_font_slant(mp,f)!=0 ) {
4535 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,f)) ;
4537 if ( mp_fm_font_extend(mp,f)!=0 ) {
4538 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
4542 @ @<Include encodings and fonts for edge structure~|h|@>=
4543 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
4544 @<Embed fonts that are available@>
4546 @ @<Embed fonts that are available@>=
4548 next_size=0;
4549 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
4550 do {
4551 done_fonts=true;
4552 for (f=null_font+1;f<=mp->last_fnum;f++) {
4553 if ( cur_fsize[f]!=null ) {
4554 if (prologues==3 ) {
4555 if ( ! mp_do_ps_font(mp,f) ) {
4556 if ( mp_has_fm_entry(mp,f, NULL) ) {
4557 mp_error(mp, "Font embedding failed", NULL, true);
4561 if (cur_fsize[f]==mp_void)
4562 cur_fsize[f]=null;
4563 else
4564 cur_fsize[f]=mp_link(cur_fsize[f]);
4565 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
4568 if ( ! done_fonts )
4569 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
4570 that size index@>;
4571 } while (! done_fonts);
4574 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
4576 next_size++;
4577 mp_apply_mark_string_chars(mp, h, next_size);
4580 @ We also need to keep track of which characters are used in text nodes
4581 in the edge structure that is being shipped out. This is done by procedures
4582 that use the left-over |b3| field in the |char_info| words; i.e.,
4583 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
4585 @<Types...@>=
4586 enum mp_char_mark_state {mp_unused=0, mp_used};
4588 @ @<Declarations@>=
4589 static void mp_mark_string_chars (MP mp,font_number f, char *s, size_t l) ;
4591 @ @c
4592 void mp_mark_string_chars (MP mp,font_number f, char *s, size_t l) {
4593 integer b; /* |char_base[f]| */
4594 int bc,ec; /* only characters between these bounds are marked */
4595 unsigned char *k; /* an index into string |s| */
4596 b=mp->char_base[f];
4597 bc=(int)mp->font_bc[f];
4598 ec=(int)mp->font_ec[f];
4599 k=(unsigned char *)s;
4600 while (l-->0){
4601 if ( (*k>=bc)&&(*k<=ec) )
4602 mp->font_info[b+*k].qqqq.b3=mp_used;
4603 k++;
4608 @ @<Declarations@>=
4609 static void mp_unmark_font (MP mp,font_number f) ;
4611 @ @c
4612 void mp_unmark_font (MP mp,font_number f) {
4613 int k; /* an index into |font_info| */
4614 for (k= mp->char_base[f]+mp->font_bc[f];
4615 k<=mp->char_base[f]+mp->font_ec[f];
4616 k++)
4617 mp->font_info[k].qqqq.b3=mp_unused;
4621 @ @<Declarations@>=
4622 static void mp_print_improved_prologue (MP mp, mp_edge_object *h, int p1, int procset) ;
4624 @ @c
4625 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
4626 quarterword next_size; /* the size index for fonts being listed */
4627 mp_node *cur_fsize; /* current positions in |font_sizes| */
4628 boolean done_fonts; /* have we finished listing the fonts in the header? */
4629 font_number f; /* a font number for loops */
4630 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(mp_node));
4631 mp_list_used_resources(mp, prologues, procset);
4632 mp_list_supplied_resources(mp, prologues, procset);
4633 mp_list_needed_resources(mp, prologues);
4634 mp_ps_print_nl(mp, "%%EndComments");
4635 mp_ps_print_nl(mp, "%%BeginProlog");
4636 if ( procset>0 )
4637 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4638 else
4639 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
4640 mp_ps_print_nl(mp, "/bd{bind def}bind def"
4641 "/fshow {exch findfont exch scalefont setfont show}bd");
4642 if ( procset>0 ) @<Print the procset@>;
4643 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
4644 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
4645 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
4646 "/fmd{/FontMatrix exch def}bd");
4647 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
4648 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
4649 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
4650 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
4651 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
4652 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
4653 mp_ps_print_nl(mp, "%%EndResource");
4654 @<Include encodings and fonts for edge structure~|h|@>;
4655 mp_ps_print_nl(mp, "%%EndProlog");
4656 mp_ps_print_nl(mp, "%%BeginSetup");
4657 mp_ps_print_ln(mp);
4658 for (f=null_font+1;f<=mp->last_fnum;f++) {
4659 if ( mp_has_font_size(mp,f) ) {
4660 if ( mp_has_fm_entry(mp,f,NULL) ) {
4661 mp_write_font_definition(mp,f, prologues);
4662 mp_ps_name_out(mp, mp->font_name[f],true);
4663 mp_ps_print_defined_name(mp,f, prologues);
4664 mp_ps_print(mp, " def");
4665 } else {
4666 char s[256];
4667 mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
4668 mp_warn(mp,s);
4669 mp_ps_name_out(mp, mp->font_name[f],true);
4670 mp_ps_name_out(mp, mp->font_name[f],true);
4671 mp_ps_print(mp, " def");
4673 mp_ps_print_ln(mp);
4676 mp_ps_print_nl(mp, "%%EndSetup");
4677 mp_ps_print_nl(mp, "%%Page: 1 1");
4678 mp_ps_print_ln(mp);
4679 mp_xfree(cur_fsize);
4682 @ @<Declarations@>=
4683 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues);
4688 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) {
4689 quarterword next_size; /* the size index for fonts being listed */
4690 mp_node *cur_fsize; /* current positions in |font_sizes| */
4691 int ff; /* a loop counter */
4692 boolean done_fonts; /* have we finished listing the fonts in the header? */
4693 font_number f; /* a font number for loops */
4694 int ds; /* design size and scale factor for a text node, scaled */
4695 int ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
4696 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(mp_node));
4697 if ( prologues>0 ) {
4698 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
4699 |font_sizes| and eliminate duplicates@>;
4700 } else {
4701 next_size=0;
4702 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
4703 do { done_fonts=true;
4704 for (f=null_font+1;f<=mp->last_fnum;f++) {
4705 if ( cur_fsize[f]!=null ) {
4706 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
4708 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
4710 if ( ! done_fonts ) {
4711 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
4712 that size index@>;
4714 } while (! done_fonts);
4716 mp_xfree(cur_fsize);
4717 return (font_number)ldf;
4720 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
4721 for (f=null_font+1;f<= mp->last_fnum;f++)
4722 cur_fsize[f]=mp->font_sizes[f]
4724 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
4725 so we carefully remove duplicates. There is no harm in using a slow, brute-force
4726 search.
4728 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
4730 ldf=null_font;
4731 for (f=null_font+1;f<= mp->last_fnum;f++) {
4732 if ( mp->font_sizes[f]!=null ) {
4733 if ( ldf==null_font )
4734 mp_ps_print_nl(mp, "%%DocumentFonts:");
4735 for (ff=ldf;ff>=null_font;ff--) {
4736 if ( mp->font_sizes[ff]!=null )
4737 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
4738 goto FOUND;
4740 if ( (size_t)mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
4741 mp_ps_print_nl(mp, "%%+");
4742 mp_ps_print_char(mp, ' ');
4743 mp_ps_print(mp, mp->font_ps_name[f]);
4744 ldf=(int)f;
4745 FOUND:
4751 @ @c
4752 static void mp_hex_digit_out (MP mp,quarterword d) {
4753 if ( d<10 ) mp_ps_print_char(mp, d+'0');
4754 else mp_ps_print_char(mp, d+'a'-10);
4757 @ We output the marks as a hexadecimal bit string starting at
4758 |font_bc[f]|.
4760 @<Declarations@>=
4761 static halfword mp_ps_marks_out (MP mp,font_number f);
4766 static halfword mp_ps_marks_out (MP mp,font_number f) {
4767 eight_bits bc,ec; /* only encode characters between these bounds */
4768 int p; /* |font_info| index for the current character */
4769 int d; /* used to construct a hexadecimal digit */
4770 unsigned b; /* used to construct a hexadecimal digit */
4771 bc=mp->font_bc[f];
4772 ec=mp->font_ec[f];
4773 @<Restrict the range |bc..ec| so that it contains no unused characters
4774 at either end@>;
4775 @<Print the initial label indicating that the bitmap starts at |bc|@>;
4776 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
4777 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
4778 p++; ec++;
4780 return (ec+1);
4783 @ We could save time by setting the return value before the loop that
4784 decrements |ec|, but there is no point in being so tricky.
4786 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
4787 p=mp->char_base[f]+bc;
4788 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
4789 p++; bc++;
4791 p=mp->char_base[f]+ec;
4792 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
4793 p--; ec--;
4796 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
4797 mp_ps_print_char(mp, ' ');
4798 mp_hex_digit_out(mp, (quarterword)(bc / 16));
4799 mp_hex_digit_out(mp, (quarterword)(bc % 16));
4800 mp_ps_print_char(mp, ':')
4804 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
4805 b=8; d=0;
4806 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
4807 if ( b==0 ) {
4808 mp_hex_digit_out(mp, (quarterword)d);
4809 d=0; b=8;
4811 if ( mp->font_info[p].qqqq.b3!=mp_unused )
4812 d+=(int)b;
4813 b=b>>1;
4815 mp_hex_digit_out(mp, (quarterword)d)
4818 @ Here is a simple function that determines whether there are any marked
4819 characters in font~|f|.
4821 @<Declarations@>=
4822 static boolean mp_check_ps_marks (MP mp,font_number f) ;
4824 @ @c
4825 static boolean mp_check_ps_marks (MP mp,font_number f) {
4826 int p; /* |font_info| index for the current character */
4827 for (p=mp->char_base[f];p<=mp->char_base[f]+mp->font_ec[f];p++) {
4828 if ( mp->font_info[p].qqqq.b3==mp_used )
4829 return true;
4831 return false;
4835 @ There used to be a check against |emergency_line_length| here, because
4836 it was believed that processing programs might not know how to deal with
4837 long lines. Nowadays (1.204), we trust backends to do the right thing.
4839 @d mp_link(A) (A)->link /* the |link| field of a memory word */
4840 @d sc_factor(A) ((mp_font_size_node)(A))->sc_factor_ /* the scale factor stored in a font size node */
4842 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
4844 if ( mp_check_ps_marks(mp, f ) ) {
4845 double dds;
4846 mp_ps_print_nl(mp, "%*Font: ");
4847 mp_ps_print(mp, mp->font_name[f]);
4848 mp_ps_print_char(mp, ' ');
4849 ds=(mp->font_dsize[f] + 8) / 16.0;
4850 dds = (double)ds / 65536.0;
4851 mp_ps_print_double(mp, mp_take_double(mp, dds, sc_factor(cur_fsize[f])));
4852 mp_ps_print_char(mp, ' ');
4853 mp_ps_print_double(mp, dds);
4854 mp_ps_marks_out(mp, f );
4856 cur_fsize[f]=mp_link(cur_fsize[f]);
4859 @ @<Print the procset@>=
4861 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
4862 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
4863 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
4864 "/p{closepath}bd/n{newpath}bd");
4865 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
4866 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
4867 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
4868 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
4869 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4873 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4874 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
4875 might not have reasonable |font_ps_name| entries, but we just charge ahead
4876 anyway. The user should not make \&{prologues} positive if this will cause
4877 trouble.
4878 @:prologues_}{\&{prologues} primitive@>
4880 @<Declarations@>=
4881 static void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset);
4883 @ @c
4884 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
4885 font_number f;
4886 font_number ldf ;
4887 ldf = mp_print_font_comments (mp, h, prologues);
4888 mp_ps_print_ln(mp);
4889 if ( (prologues==1) && (mp->last_ps_fnum==0) )
4890 mp_read_psname_table(mp);
4891 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4892 if ( (prologues>0)||(procset>0) ) {
4893 if ( ldf!=null_font ) {
4894 if ( prologues>0 ) {
4895 for (f=null_font+1;f<=mp->last_fnum;f++) {
4896 if ( mp_has_font_size(mp,f) ) {
4897 mp_ps_name_out(mp, mp->font_name[f],true);
4898 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4899 mp_ps_print(mp, " def");
4900 mp_ps_print_ln(mp);
4903 if ( procset==0 ) {
4904 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4905 mp_ps_print_ln(mp);
4909 if (procset>0 ) {
4910 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4911 if ( (prologues>0)&&(ldf!=null_font) )
4912 mp_ps_print_nl(mp,
4913 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4914 else
4915 mp_ps_print_nl(mp, "/bd{bind def}bind def");
4916 @<Print the procset@>;
4917 mp_ps_print_nl(mp, "%%EndResource");
4918 mp_ps_print_ln(mp);
4921 mp_ps_print(mp, "%%EndProlog");
4922 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4925 @ \MP\ used to have one single routine to print to both `write' files
4926 and the PostScript output. Web2c redefines ``Character |k| cannot be
4927 printed'', and that resulted in some bugs where 8-bit characters were
4928 written to the PostScript file (reported by Wlodek Bzyl).
4930 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4931 a plain space, since that makes it easier to parse the result file
4932 for postprocessing.
4934 @<Character |k| is not allowed in PostScript output@>=
4935 (k<=' ')||(k>'~')
4937 @ We often need to print a pair of coordinates.
4940 void mp_ps_pair_out (MP mp, double x, double y) {
4941 ps_room(26);
4942 mp_ps_print_double(mp, x); mp_ps_print_char(mp, ' ');
4943 mp_ps_print_double(mp, y); mp_ps_print_char(mp, ' ');
4946 @ @<Declarations@>=
4947 static void mp_ps_pair_out (MP mp, double x, double y) ;
4949 @ @c
4950 void mp_ps_print_cmd (MP mp, const char *l, const char *s) {
4951 if ( number_positive (internal_value(mp_procset))) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4952 else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4955 @ @<Declarations@>=
4956 static void mp_ps_print_cmd (MP mp, const char *l, const char *s) ;
4958 @ @c
4959 void mp_ps_string_out (MP mp, const char *s, size_t l) {
4960 ASCII_code k; /* bits to be converted to octal */
4961 mp_ps_print(mp, "(");
4962 while (l-->0) {
4963 k=(ASCII_code)*s++;
4964 if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4965 mp_ps_print_char(mp, '\\');
4966 mp_ps_print_ln(mp);
4968 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4969 mp_ps_print_char(mp, '\\');
4970 mp_ps_print_char(mp, '0'+(k / 64));
4971 mp_ps_print_char(mp, '0'+((k / 8) % 8));
4972 mp_ps_print_char(mp, '0'+(k % 8));
4973 } else {
4974 if ( (k=='(')||(k==')')||(k=='\\') )
4975 mp_ps_print_char(mp, '\\');
4976 mp_ps_print_char(mp, k);
4979 mp_ps_print_char(mp, ')');
4982 @ @<Declarations@>=
4983 static void mp_ps_string_out (MP mp, const char *s, size_t l) ;
4985 @ This is a define because the function does not use its |mp| argument.
4987 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4990 static boolean mp_do_is_ps_name (char *s) {
4991 ASCII_code k; /* the character being checked */
4992 while ((k=(ASCII_code)*s++)) {
4993 if ( (k<=' ')||(k>'~') ) return false;
4994 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4995 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4997 return true;
5000 @ @<Declarations@>=
5001 static void mp_ps_name_out (MP mp, char *s, boolean lit) ;
5003 @ @c
5004 void mp_ps_name_out (MP mp, char *s, boolean lit) {
5005 ps_room(strlen(s)+2);
5006 mp_ps_print_char(mp, ' ');
5007 if ( mp_is_ps_name(mp, s) ) {
5008 if ( lit ) mp_ps_print_char(mp, '/');
5009 mp_ps_print(mp, s);
5010 } else {
5011 mp_ps_string_out(mp, s, strlen(s));
5012 if ( ! lit ) mp_ps_print(mp, "cvx ");
5013 mp_ps_print(mp, "cvn");
5018 @ These special comments described in the {\sl PostScript Language Reference
5019 Manual}, 2nd.~edition are understood by some \ps-reading programs.
5020 We can't normally output ``conforming'' \ps\ because
5021 the structuring conventions don't allow us to say ``Please make sure the
5022 following characters are downloaded and define the \.{fshow} macro to access
5023 them.''
5025 The exact bounding box is written out if |mp_prologues<0|, although this
5026 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
5027 accurately. (Overfull boxes are avoided if an illustration is made to
5028 match a given \.{\char`\\hsize}.)
5030 @<Declarations@>=
5031 static void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues);
5033 @ @c
5034 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) {
5035 int t; /* scaled */
5036 char *s;
5037 mp_ps_print(mp, "%!PS");
5038 if ( prologues>0 )
5039 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
5040 mp_ps_print_nl(mp, "%%BoundingBox: ");
5041 if ( hh->minx>hh->maxx) {
5042 mp_ps_print(mp, "0 0 0 0");
5043 } else if ( prologues<0 ) {
5044 mp_ps_pair_out(mp, hh->minx,hh->miny);
5045 mp_ps_pair_out(mp, hh->maxx,hh->maxy);
5046 } else {
5047 mp_ps_pair_out(mp, floor(hh->minx),floor(hh->miny));
5048 mp_ps_pair_out(mp, -floor(-hh->maxx),-floor(-hh->maxy));
5050 mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
5051 if ( hh->minx>hh->maxx ) {
5052 mp_ps_print(mp, "0 0 0 0");
5053 } else {
5054 mp_ps_pair_out(mp, hh->minx,hh->miny);
5055 mp_ps_pair_out(mp, hh->maxx,hh->maxy);
5057 mp_ps_print_nl(mp, "%%Creator: MetaPost ");
5058 s = mp_metapost_version();
5059 mp_ps_print(mp, s);
5060 mp_xfree(s);
5061 mp_ps_print_nl(mp, "%%CreationDate: ");
5062 mp_ps_print_int(mp, round_unscaled(internal_value(mp_year)));
5063 mp_ps_print_char(mp, '.');
5064 mp_ps_print_dd(mp, round_unscaled(internal_value(mp_month)));
5065 mp_ps_print_char(mp, '.');
5066 mp_ps_print_dd(mp, round_unscaled(internal_value(mp_day)));
5067 mp_ps_print_char(mp, ':');
5068 t = round_unscaled(internal_value(mp_time));
5069 mp_ps_print_dd(mp, t / 60);
5070 mp_ps_print_dd(mp, t % 60);
5071 mp_ps_print_nl(mp, "%%Pages: 1");
5074 @ The most important output procedure is the one that gives the \ps\ version of
5075 a \MP\ path.
5077 @(mplibps.h@>=
5078 #ifndef MPLIBPS_H
5079 #define MPLIBPS_H 1
5080 #include "mplib.h"
5081 @<Internal Postscript header information@>
5082 #endif
5085 @ @<Types...@>=
5086 #define gr_left_type(A) (A)->data.types.left_type
5087 #define gr_right_type(A) (A)->data.types.right_type
5088 #define gr_x_coord(A) (A)->x_coord
5089 #define gr_y_coord(A) (A)->y_coord
5090 #define gr_left_x(A) (A)->left_x
5091 #define gr_left_y(A) (A)->left_y
5092 #define gr_right_x(A) (A)->right_x
5093 #define gr_right_y(A) (A)->right_y
5094 #define gr_next_knot(A) (A)->next
5095 #define gr_originator(A) (A)->originator
5097 @ If we want to duplicate a knot node, we can say |copy_knot|:
5100 static mp_gr_knot mp_gr_copy_knot (MP mp, mp_gr_knot p) {
5101 mp_gr_knot q; /* the copy */
5102 q = mp_xmalloc(mp, 1, sizeof (struct mp_gr_knot_data));
5103 memcpy(q,p,sizeof (struct mp_gr_knot_data));
5104 gr_next_knot(q)=NULL;
5105 return q;
5108 @ The |copy_path| routine makes a clone of a given path.
5111 static mp_gr_knot mp_gr_copy_path (MP mp, mp_gr_knot p) {
5112 mp_gr_knot q, pp, qq; /* for list manipulation */
5113 if (p==NULL)
5114 return NULL;
5115 q=mp_gr_copy_knot(mp, p);
5116 qq=q;
5117 pp=gr_next_knot(p);
5118 while ( pp!=p ) {
5119 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
5120 qq=gr_next_knot(qq);
5121 pp=gr_next_knot(pp);
5123 gr_next_knot(qq)=q;
5124 return q;
5127 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
5128 calling the following subroutine.
5130 @<Declarations@>=
5131 void mp_do_gr_toss_knot_list (mp_gr_knot p) ;
5134 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
5137 void mp_do_gr_toss_knot_list (mp_gr_knot p) {
5138 mp_gr_knot q; /* the node being freed */
5139 mp_gr_knot r; /* the next node */
5140 if (p==NULL)
5141 return;
5142 q=p;
5143 do {
5144 r=gr_next_knot(q);
5145 mp_xfree(q); q=r;
5146 } while (q!=p);
5151 @ @c
5152 static void mp_gr_ps_path_out (MP mp, mp_gr_knot h) {
5153 mp_gr_knot p, q; /* for scanning the path */
5154 double d; /* a temporary value */
5155 boolean curved; /* |true| unless the cubic is almost straight */
5156 ps_room(40);
5157 mp_ps_print_cmd(mp, "newpath ","n ");
5158 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
5159 mp_ps_print_cmd(mp, "moveto","m");
5160 p=h;
5161 do {
5162 if ( gr_right_type(p)==mp_endpoint ) {
5163 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
5164 return;
5166 q=gr_next_knot(p);
5167 @<Start a new line and print the \ps\ commands for the curve from
5168 |p| to~|q|@>;
5169 p=q;
5170 } while (p!=h);
5171 mp_ps_print_cmd(mp, " closepath"," p");
5174 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
5175 curved=true;
5176 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
5177 mp_ps_print_ln(mp);
5178 if ( curved ){
5179 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
5180 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
5181 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
5182 mp_ps_print_cmd(mp, "curveto","c");
5183 } else if ( q!=h ){
5184 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
5185 mp_ps_print_cmd(mp, "lineto","l");
5188 @ Two types of straight lines come up often in \MP\ paths:
5189 cubics with zero initial and final velocity as created by |make_path| or
5190 |make_envelope|, and cubics with control points uniformly spaced on a line
5191 as created by |make_choices|.
5193 @d bend_tolerance (131/65536.0) /* allow rounding error of $2\cdot10^{-3}$ */
5195 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
5196 if ( gr_right_x(p)==gr_x_coord(p) )
5197 if ( gr_right_y(p)==gr_y_coord(p) )
5198 if ( gr_left_x(q)==gr_x_coord(q) )
5199 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
5200 d=gr_left_x(q)-gr_right_x(p);
5201 if ( fabs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
5202 if ( fabs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
5203 { d=gr_left_y(q)-gr_right_y(p);
5204 if ( fabs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
5205 if ( fabs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
5208 @ The colored objects use a struct with anonymous fields to express the color parts:
5210 @<Internal Postscript header information@>=
5211 typedef struct {
5212 double a_val, b_val, c_val, d_val;
5213 } mp_color;
5215 @ The exported form of a dash pattern is simpler than the internal
5216 format, it is closely modelled to the PostScript model. The array of
5217 dashes is ended by a single negative value, because this is not
5218 allowed in PostScript.
5220 @<Internal Postscript header information@>=
5221 typedef struct {
5222 double offset;
5223 double *array;
5224 } mp_dash_object ;
5228 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
5230 @<Declarations@>=
5231 static void mp_do_gr_toss_dashes(mp_dash_object *dl);
5233 @ @c
5234 void mp_do_gr_toss_dashes(mp_dash_object *dl) {
5235 if (dl==NULL)
5236 return;
5237 mp_xfree(dl->array);
5238 mp_xfree(dl);
5242 @ @c
5243 static mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) {
5244 mp_dash_object *q = NULL;
5245 (void)mp;
5246 if (dl==NULL)
5247 return NULL;
5248 q = mp_xmalloc(mp, 1, sizeof (mp_dash_object));
5249 memcpy (q,dl,sizeof(mp_dash_object));
5250 if (dl->array != NULL) {
5251 size_t i = 0;
5252 while (*(dl->array+i) != -1) i++;
5253 q->array = mp_xmalloc(mp, i, sizeof (double));
5254 memcpy(q->array,dl->array, (i*sizeof(double)));
5256 return q;
5260 @ Now for outputting the actual graphic objects. First, set up some
5261 structures and access macros.
5263 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
5265 @<Types...@>=
5266 #define gr_type(A) (A)->type
5267 #define gr_link(A) (A)->next
5268 #define gr_color_model(A) (A)->color_model
5269 #define gr_red_val(A) (A)->color.a_val
5270 #define gr_green_val(A) (A)->color.b_val
5271 #define gr_blue_val(A) (A)->color.c_val
5272 #define gr_cyan_val(A) (A)->color.a_val
5273 #define gr_magenta_val(A) (A)->color.b_val
5274 #define gr_yellow_val(A) (A)->color.c_val
5275 #define gr_black_val(A) (A)->color.d_val
5276 #define gr_grey_val(A) (A)->color.a_val
5277 #define gr_path_p(A) (A)->path_p
5278 #define gr_htap_p(A) ((mp_fill_object *)A)->htap_p
5279 #define gr_pen_p(A) (A)->pen_p
5280 #define gr_ljoin_val(A) (A)->ljoin
5281 #define gr_lcap_val(A) ((mp_stroked_object *)A)->lcap
5282 #define gr_miterlim_val(A) (A)->miterlim
5283 #define gr_pre_script(A) (A)->pre_script
5284 #define gr_post_script(A) (A)->post_script
5285 #define gr_dash_p(A) ((mp_stroked_object *)A)->dash_p
5286 #define gr_size_index(A) ((mp_text_object *)A)->size_index
5287 #define gr_text_p(A) ((mp_text_object *)A)->text_p
5288 #define gr_text_l(A) ((mp_text_object *)A)->text_l
5289 #define gr_font_n(A) ((mp_text_object *)A)->font_n
5290 #define gr_font_name(A) ((mp_text_object *)A)->font_name
5291 #define gr_font_dsize(A) ((mp_text_object *)A)->font_dsize
5292 #define gr_width_val(A) ((mp_text_object *)A)->width
5293 #define gr_height_val(A) ((mp_text_object *)A)->height
5294 #define gr_depth_val(A) ((mp_text_object *)A)->depth
5295 #define gr_tx_val(A) ((mp_text_object *)A)->tx
5296 #define gr_ty_val(A) ((mp_text_object *)A)->ty
5297 #define gr_txx_val(A) ((mp_text_object *)A)->txx
5298 #define gr_txy_val(A) ((mp_text_object *)A)->txy
5299 #define gr_tyx_val(A) ((mp_text_object *)A)->tyx
5300 #define gr_tyy_val(A) ((mp_text_object *)A)->tyy
5302 @ @<Internal Postscript header information@>=
5303 #define GRAPHIC_BODY \
5304 int type; \
5305 struct mp_graphic_object * next
5307 typedef struct mp_graphic_object {
5308 GRAPHIC_BODY;
5309 } mp_graphic_object;
5311 typedef struct mp_text_object {
5312 GRAPHIC_BODY;
5313 char *pre_script;
5314 char *post_script;
5315 mp_color color;
5316 unsigned char color_model;
5317 unsigned char size_index;
5318 char *text_p;
5319 size_t text_l;
5320 char *font_name ;
5321 double font_dsize ;
5322 unsigned int font_n ;
5323 double width ;
5324 double height ;
5325 double depth ;
5326 double tx ;
5327 double ty ;
5328 double txx ;
5329 double txy ;
5330 double tyx ;
5331 double tyy ;
5332 } mp_text_object;
5334 typedef struct mp_fill_object {
5335 GRAPHIC_BODY;
5336 char *pre_script;
5337 char *post_script;
5338 mp_color color;
5339 unsigned char color_model;
5340 unsigned char ljoin ;
5341 mp_gr_knot path_p;
5342 mp_gr_knot htap_p;
5343 mp_gr_knot pen_p;
5344 double miterlim ;
5345 } mp_fill_object;
5347 typedef struct mp_stroked_object {
5348 GRAPHIC_BODY;
5349 char *pre_script;
5350 char *post_script;
5351 mp_color color;
5352 unsigned char color_model;
5353 unsigned char ljoin ;
5354 unsigned char lcap ;
5355 mp_gr_knot path_p;
5356 mp_gr_knot pen_p;
5357 double miterlim ;
5358 mp_dash_object *dash_p;
5359 } mp_stroked_object;
5361 typedef struct mp_clip_object {
5362 GRAPHIC_BODY;
5363 mp_gr_knot path_p;
5364 } mp_clip_object;
5366 typedef struct mp_bounds_object {
5367 GRAPHIC_BODY;
5368 mp_gr_knot path_p;
5369 } mp_bounds_object;
5371 typedef struct mp_special_object {
5372 GRAPHIC_BODY;
5373 char *pre_script;
5374 } mp_special_object ;
5376 typedef struct mp_edge_object {
5377 struct mp_graphic_object * body;
5378 struct mp_edge_object * next;
5379 char * filename;
5380 MP parent;
5381 double minx, miny, maxx, maxy;
5382 double width, height, depth, ital_corr;
5383 int charcode;
5384 } mp_edge_object;
5386 @ @<Exported function headers@>=
5387 mp_graphic_object *mp_new_graphic_object(MP mp, int type);
5389 @ @c
5390 mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
5391 mp_graphic_object *p;
5392 size_t size ;
5393 switch (type) {
5394 case mp_fill_code: size = sizeof(mp_fill_object); break;
5395 case mp_stroked_code: size = sizeof(mp_stroked_object); break;
5396 case mp_text_code: size = sizeof(mp_text_object); break;
5397 case mp_start_clip_code: size = sizeof(mp_clip_object); break;
5398 case mp_start_bounds_code: size = sizeof(mp_bounds_object); break;
5399 case mp_special_code: size = sizeof(mp_special_object); break;
5400 default: size = sizeof(mp_graphic_object); break;
5402 p = (mp_graphic_object *)mp_xmalloc(mp,1,size);
5403 memset(p,0,size);
5404 gr_type(p) = type;
5405 return p;
5408 @ We need to keep track of several parameters from the \ps\ graphics state.
5409 @^graphics state@>
5410 This allows us to be sure that \ps\ has the correct values when they are
5411 needed without wasting time and space setting them unnecessarily.
5413 @d gs_red mp->ps->gs_state->red_field
5414 @d gs_green mp->ps->gs_state->green_field
5415 @d gs_blue mp->ps->gs_state->blue_field
5416 @d gs_black mp->ps->gs_state->black_field
5417 @d gs_colormodel mp->ps->gs_state->colormodel_field
5418 @d gs_ljoin mp->ps->gs_state->ljoin_field
5419 @d gs_lcap mp->ps->gs_state->lcap_field
5420 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
5421 @d gs_miterlim mp->ps->gs_state->miterlim_field
5422 @d gs_dash_p mp->ps->gs_state->dash_p_field
5423 @d gs_dash_init_done mp->ps->gs_state->dash_done_field
5424 @d gs_previous mp->ps->gs_state->previous_field
5425 @d gs_width mp->ps->gs_state->width_field
5427 @<Types...@>=
5428 typedef struct _gs_state {
5429 double red_field ;
5430 double green_field ;
5431 double blue_field ;
5432 double black_field ;
5433 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
5434 quarterword colormodel_field ;
5435 /* the current colormodel */
5436 quarterword ljoin_field ;
5437 quarterword lcap_field ;
5438 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
5439 quarterword adj_wx_field ;
5440 /* what resolution-dependent adjustment applies to the width */
5441 double miterlim_field ;
5442 /* the value from the last \&{setmiterlimit} command */
5443 mp_dash_object * dash_p_field ;
5444 /* edge structure for last \&{setdash} command */
5445 boolean dash_done_field ; /* to test for initial \&{setdash} */
5446 struct _gs_state * previous_field ;
5447 /* backlink to the previous |_gs_state| structure */
5448 double width_field ;
5449 /* width setting or $-1$ if no \&{setlinewidth} command so far */
5450 } _gs_state;
5453 @ @<Glob...@>=
5454 struct _gs_state * gs_state;
5456 @ @<Set init...@>=
5457 mp->ps->gs_state=NULL;
5459 @ @<Dealloc variables@>=
5460 mp_xfree(mp->ps->gs_state);
5462 @ To avoid making undue assumptions about the initial graphics state, these
5463 parameters are given special values that are guaranteed not to match anything
5464 in the edge structure being shipped out. On the other hand, the initial color
5465 should be black so that the translation of an all-black picture will have no
5466 \&{setcolor} commands. (These would be undesirable in a font application.)
5467 Hence we use |c=0| when initializing the graphics state and we use |c<0|
5468 to recover from a situation where we have lost track of the graphics state.
5470 @d mp_void (mp_node)(null+1) /* a null pointer different from |null| */
5472 @c static void mp_gs_unknown_graphics_state (MP mp, int c) {
5473 struct _gs_state *p; /* to shift graphic states around */
5474 if ( (c==0)||(c==-1) ) {
5475 if ( mp->ps->gs_state==NULL ) {
5476 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
5477 gs_previous=NULL;
5478 } else {
5479 while ( gs_previous!=NULL ) {
5480 p = gs_previous;
5481 mp_xfree(mp->ps->gs_state);
5482 mp->ps->gs_state=p;
5485 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
5486 gs_colormodel=mp_uninitialized_model;
5487 gs_ljoin=3;
5488 gs_lcap=3;
5489 gs_miterlim=0.0;
5490 gs_dash_p=NULL;
5491 gs_dash_init_done=false;
5492 gs_width=-1.0;
5493 } else if ( c==1 ) {
5494 p= mp->ps->gs_state;
5495 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
5496 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
5497 gs_previous = p;
5498 } else if ( c==2 ) {
5499 p = gs_previous;
5500 mp_xfree(mp->ps->gs_state);
5501 mp->ps->gs_state=p;
5506 @ When it is time to output a graphical object, |fix_graphics_state| ensures
5507 that \ps's idea of the graphics state agrees with what is stored in the object.
5509 @<Declarations@>=
5510 static void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ;
5512 @ @c
5513 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) {
5514 /* get ready to output graphical object |p| */
5515 mp_gr_knot pp, path_p; /* for list manipulation */
5516 mp_dash_object *hh;
5517 double wx,wy,ww; /* dimensions of pen bounding box */
5518 quarterword adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
5519 double tx,ty; /* temporaries for computing |adj_wx| */
5520 if ( gr_has_color(p) )
5521 @<Make sure \ps\ will use the right color for object~|p|@>;
5522 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) {
5523 if (gr_type(p)==mp_fill_code) {
5524 pp = gr_pen_p((mp_fill_object *)p);
5525 path_p = gr_path_p((mp_fill_object *)p);
5526 } else {
5527 pp = gr_pen_p((mp_stroked_object *)p);
5528 path_p = gr_path_p((mp_stroked_object *)p);
5530 if ( pp!=NULL )
5531 if ( pen_is_elliptical(pp) ) {
5532 @<Generate \ps\ code that sets the stroke width to the
5533 appropriate rounded value@>;
5534 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
5535 @<Decide whether the line cap parameter matters and set it if necessary@>;
5536 @<Set the other numeric parameters as needed for object~|p|@>;
5539 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
5542 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
5543 if ( gr_type(p)==mp_stroked_code ) {
5544 mp_stroked_object *ts = (mp_stroked_object *)p;
5545 if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) )
5546 if ( gs_lcap!=(quarterword)gr_lcap_val(ts) ) {
5547 ps_room(13);
5548 mp_ps_print_char(mp, ' ');
5549 mp_ps_print_char(mp, '0'+gr_lcap_val(ts));
5550 mp_ps_print_cmd(mp, " setlinecap"," lc");
5551 gs_lcap=(quarterword)gr_lcap_val(ts);
5556 @d set_ljoin_miterlim(p)
5557 if ( gs_ljoin!=(quarterword)gr_ljoin_val(p) ) {
5558 ps_room(14);
5559 mp_ps_print_char(mp, ' ');
5560 mp_ps_print_char(mp, '0'+gr_ljoin_val(p));
5561 mp_ps_print_cmd(mp, " setlinejoin"," lj");
5562 gs_ljoin=(quarterword)gr_ljoin_val(p);
5564 if ( gs_miterlim!=gr_miterlim_val(p) ) {
5565 ps_room(27);
5566 mp_ps_print_char(mp, ' ');
5567 mp_ps_print_double(mp, gr_miterlim_val(p));
5568 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
5569 gs_miterlim=gr_miterlim_val(p);
5572 @<Set the other numeric parameters as needed for object~|p|@>=
5573 if ( gr_type(p)==mp_stroked_code ) {
5574 mp_stroked_object *ts = (mp_stroked_object *)p;
5575 set_ljoin_miterlim(ts);
5576 } else {
5577 mp_fill_object *ts = (mp_fill_object *)p;
5578 set_ljoin_miterlim(ts);
5582 @d set_color_objects(pq)
5583 object_color_model = pq->color_model;
5584 object_color_a = pq->color.a_val;
5585 object_color_b = pq->color.b_val;
5586 object_color_c = pq->color.c_val;
5587 object_color_d = pq->color.d_val;
5589 @<Make sure \ps\ will use the right color for object~|p|@>=
5591 int object_color_model;
5592 double object_color_a, object_color_b, object_color_c, object_color_d ;
5593 if (gr_type(p) == mp_fill_code) {
5594 mp_fill_object *pq = (mp_fill_object *)p;
5595 set_color_objects(pq);
5596 } else if (gr_type(p) == mp_stroked_code) {
5597 mp_stroked_object *pq = (mp_stroked_object *)p;
5598 set_color_objects(pq);
5599 } else {
5600 mp_text_object *pq = (mp_text_object *)p;
5601 set_color_objects(pq);
5604 if ( object_color_model==mp_rgb_model) {
5605 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)||
5606 (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) {
5607 gs_red = object_color_a;
5608 gs_green = object_color_b;
5609 gs_blue = object_color_c;
5610 gs_black = -1.0;
5611 gs_colormodel=mp_rgb_model;
5612 { ps_room(36);
5613 mp_ps_print_char(mp, ' ');
5614 mp_ps_print_double(mp, gs_red); mp_ps_print_char(mp, ' ');
5615 mp_ps_print_double(mp, gs_green); mp_ps_print_char(mp, ' ');
5616 mp_ps_print_double(mp, gs_blue);
5617 mp_ps_print_cmd(mp, " setrgbcolor", " R");
5620 } else if ( object_color_model==mp_cmyk_model) {
5621 if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)||
5622 (gs_blue!=object_color_c)||(gs_black!=object_color_d)||
5623 (gs_colormodel!=mp_cmyk_model) ) {
5624 gs_red = object_color_a;
5625 gs_green = object_color_b;
5626 gs_blue = object_color_c;
5627 gs_black = object_color_d;
5628 gs_colormodel=mp_cmyk_model;
5629 { ps_room(45);
5630 mp_ps_print_char(mp, ' ');
5631 mp_ps_print_double(mp, gs_red);
5632 mp_ps_print_char(mp, ' ');
5633 mp_ps_print_double(mp, gs_green);
5634 mp_ps_print_char(mp, ' ');
5635 mp_ps_print_double(mp, gs_blue);
5636 mp_ps_print_char(mp, ' ');
5637 mp_ps_print_double(mp, gs_black);
5638 mp_ps_print_cmd(mp, " setcmykcolor"," C");
5641 } else if ( object_color_model==mp_grey_model ) {
5642 if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) {
5643 gs_red = object_color_a;
5644 gs_green = -1.0;
5645 gs_blue = -1.0;
5646 gs_black = -1.0;
5647 gs_colormodel=mp_grey_model;
5648 { ps_room(16);
5649 mp_ps_print_char(mp, ' ');
5650 mp_ps_print_double(mp, gs_red);
5651 mp_ps_print_cmd(mp, " setgray"," G");
5654 } else if ( object_color_model==mp_no_model ) {
5655 gs_colormodel=mp_no_model;
5659 @ In order to get consistent widths for horizontal and vertical pen strokes, we
5660 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
5661 @:setwidth}{\&{setwidth}command@>
5662 We set |gs_width| to the ideal horizontal or vertical stroke width and then
5663 generate \ps\ code that computes the rounded value. For non-circular pens, the
5664 pen shape will be rescaled so that horizontal or vertical parts of the stroke
5665 have the computed width.
5667 Rounding the width to whole pixels is not likely to improve the appearance of
5668 diagonal or curved strokes, but we do it anyway for consistency. The
5669 \&{truncate} command generated here tends to make all the strokes a little
5670 @:truncate}{\&{truncate} command@>
5671 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
5672 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
5673 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
5674 to compute in \ps.
5676 @<Generate \ps\ code that sets the stroke width...@>=
5677 @<Set |wx| and |wy| to the width and height of the bounding box for
5678 |pen_p(p)|@>;
5679 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
5680 important and set |adj_wx| and |ww| accordingly@>;
5681 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
5682 if ( adj_wx != 0 ) {
5683 ps_room(13);
5684 mp_ps_print_char(mp, ' '); mp_ps_print_double(mp, ww);
5685 mp_ps_print_cmd(mp,
5686 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
5687 } else {
5688 if (number_positive (internal_value(mp_procset)) ) {
5689 ps_room(13);
5690 mp_ps_print_char(mp, ' ');
5691 mp_ps_print_double(mp, ww);
5692 mp_ps_print(mp, " vlw");
5693 } else {
5694 ps_room(15);
5695 mp_ps_print(mp, " 0 "); mp_ps_print_double(mp, ww);
5696 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
5699 gs_width = ww;
5700 gs_adj_wx = adj_wx;
5703 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
5704 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
5705 wx = fabs(gr_left_x(pp) - gr_x_coord(pp));
5706 wy = fabs(gr_right_y(pp) - gr_y_coord(pp));
5707 } else {
5708 double a, b;
5709 a = gr_left_x(pp)-gr_x_coord(pp);
5710 b = gr_right_x(pp)-gr_x_coord(pp);
5711 wx = sqrt(a*a + b*b);
5712 a = gr_left_y(pp)-gr_y_coord(pp);
5713 b = gr_right_y(pp)-gr_y_coord(pp);
5714 wy = sqrt(a*a + b*b);
5717 @ The path is considered ``essentially horizontal'' if its range of
5718 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
5719 vertical'' paths are detected similarly. This code ensures that no component
5720 of the pen transformation is more that |aspect_bound*(ww+1)|.
5722 @d aspect_bound 10.0/65536.0 /* ``less important'' of |wx|, |wy| cannot exceed the other by
5723 more than this factor */
5725 @d do_x_loc 1
5726 @d do_y_loc 2
5728 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
5729 tx=1.0/65536.0; ty=1.0/65536.0;
5730 if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound;
5731 else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound;
5732 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=0; }
5733 else { ww=wx; adj_wx=1; }
5735 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
5736 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
5737 allowable range for $x$ or~$y$. We do not need and cannot afford a full
5738 bounding-box computation.
5740 @<Declarations@>=
5741 static boolean mp_gr_coord_rangeOK (mp_gr_knot h,
5742 quarterword zoff, double dz);
5744 @ @c
5745 boolean mp_gr_coord_rangeOK (mp_gr_knot h,
5746 quarterword zoff, double dz) {
5747 mp_gr_knot p; /* for scanning the path form |h| */
5748 double zlo,zhi; /* coordinate range so far */
5749 double z; /* coordinate currently being tested */
5750 if (zoff==do_x_loc) {
5751 zlo=gr_x_coord(h);
5752 zhi=zlo;
5753 p=h;
5754 while ( gr_right_type(p)!=mp_endpoint ) {
5755 z=gr_right_x(p);
5756 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
5757 p=gr_next_knot(p); z=gr_left_x(p);
5758 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
5759 z=gr_x_coord(p);
5760 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
5761 if ( p==h ) break;
5763 } else {
5764 zlo=gr_y_coord(h);
5765 zhi=zlo;
5766 p=h;
5767 while ( gr_right_type(p)!=mp_endpoint ) {
5768 z=gr_right_y(p);
5769 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
5770 p=gr_next_knot(p); z=gr_left_y(p);
5771 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
5772 z=gr_y_coord(p);
5773 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
5774 if ( p==h ) break;
5777 return true;
5780 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
5781 if ( z<zlo ) zlo=z;
5782 else if ( z>zhi ) zhi=z;
5783 if ( zhi-zlo>dz ) return false
5785 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
5786 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
5787 @:stroke}{\&{stroke} command@>
5788 @:fill}{\&{fill} command@>
5789 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
5790 a reference.
5792 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
5793 if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) {
5794 hh=NULL;
5795 } else {
5796 hh=gr_dash_p(p);
5798 if ( hh==NULL ) {
5799 if ( gs_dash_p!=NULL || gs_dash_init_done == false) {
5800 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5801 gs_dash_p=NULL;
5802 gs_dash_init_done=true;
5804 } else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) {
5805 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
5808 @ The original code had a check here to ensure that the result from
5809 |mp_take_scaled| did not go out of bounds.
5811 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5812 { gs_dash_p=hh;
5813 if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array==NULL)) {
5814 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5815 } else {
5816 int i;
5817 ps_room(28);
5818 mp_ps_print(mp, " [");
5819 for (i=0; *(hh->array+i) != -1;i++) {
5820 ps_room(13);
5821 mp_ps_print_double(mp, *(hh->array+i));
5822 mp_ps_print_char(mp, ' ') ;
5824 ps_room(22);
5825 mp_ps_print(mp, "] ");
5826 mp_ps_print_double(mp, hh->offset);
5827 mp_ps_print_cmd(mp, " setdash"," sd");
5831 @ @<Declarations@>=
5832 static boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ;
5834 @ This function test if |h| and |hh| represent the same dash pattern.
5837 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) {
5838 boolean ret=false;
5839 int i = 0;
5840 if ( h==hh ) ret=true;
5841 else if ( (h==NULL)||(hh==NULL) ) ret=false;
5842 else if ( h->offset!=hh->offset ) ret=false;
5843 else if ( h->array == hh->array ) ret=true;
5844 else if ( h->array == NULL || hh->array == NULL) ret=false;
5845 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5846 return ret;
5849 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5851 while (*(h->array+i)!=-1 &&
5852 *(hh->array+i)!=-1 &&
5853 *(h->array+i) == *(hh->array+i)) i++;
5854 if (i>0) {
5855 if (*(h->array+i)==-1 && *(hh->array+i) == -1)
5856 ret=true;
5860 @ When stroking a path with an elliptical pen, it is necessary to transform
5861 the coordinate system so that a unit circular pen will have the desired shape.
5862 To keep this transformation local, we enclose it in a
5863 $$\&{gsave}\ldots\&{grestore}$$
5864 block. Any translation component must be applied to the path being stroked
5865 while the rest of the transformation must apply only to the pen.
5866 If |fill_also=true|, the path is to be filled as well as stroked so we must
5867 insert commands to do this after giving the path.
5869 @<Declarations@>=
5870 static void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) ;
5873 @c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) {
5874 /* generate an elliptical pen stroke from object |h| */
5875 double txx,txy,tyx,tyy; /* transformation parameters */
5876 mp_gr_knot p; /* the pen to stroke with */
5877 double d1,det; /* for tweaking transformation parameters */
5878 double s; /* also for tweaking transformation paramters */
5879 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5880 transformed=false;
5881 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5882 translation@>;
5883 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5884 if (gr_type(h)==mp_fill_code) {
5885 mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h));
5886 } else {
5887 mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h));
5889 if ( number_zero (internal_value(mp_procset))) {
5890 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5891 @<Issue \ps\ commands to transform the coordinate system@>;
5892 mp_ps_print(mp, " stroke");
5893 if ( transformed ) mp_ps_print(mp, " grestore");
5894 } else {
5895 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5896 if ( (txy!=0.0)||(tyx!=0.0) ) {
5897 mp_ps_print(mp, " [");
5898 mp_ps_pair_out(mp, txx,tyx);
5899 mp_ps_pair_out(mp, txy,tyy);
5900 mp_ps_print(mp, "0 0] t");
5901 } else if ((txx!=unity)||(tyy!=unity) ) {
5902 mp_ps_print(mp, " ");
5903 mp_ps_pair_out(mp,txx,tyy);
5904 mp_ps_print(mp, " s");
5906 mp_ps_print(mp, " S");
5907 if ( transformed ) mp_ps_print(mp, " Q");
5909 mp_ps_print_ln(mp);
5912 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5913 if (gr_type(h)==mp_fill_code) {
5914 p=gr_pen_p((mp_fill_object *)h);
5915 } else {
5916 p=gr_pen_p((mp_stroked_object *)h);
5918 txx=gr_left_x(p);
5919 tyx=gr_left_y(p);
5920 txy=gr_right_x(p);
5921 tyy=gr_right_y(p);
5922 if ( (gr_x_coord(p)!=0.0)||(gr_y_coord(p)!=0.0) ) {
5923 mp_ps_print_nl(mp, "");
5924 mp_ps_print_cmd(mp, "gsave ","q ");
5925 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5926 mp_ps_print(mp, "translate ");
5927 txx-=gr_x_coord(p);
5928 tyx-=gr_y_coord(p);
5929 txy-=gr_x_coord(p);
5930 tyy-=gr_y_coord(p);
5931 transformed=true;
5932 } else {
5933 mp_ps_print_nl(mp, "");
5935 @<Adjust the transformation to account for |gs_width| and output the
5936 initial \&{gsave} if |transformed| should be |true|@>
5940 @d mp_make_double(A,B,C) ((B)/(C))
5941 @d mp_take_double(A,B,C) ((B)*(C))
5943 @<Adjust the transformation to account for |gs_width| and output the...@>=
5944 if ( gs_width!=unity ) {
5945 if ( gs_width==0.0 ) {
5946 txx=unity; tyy=unity;
5947 } else {
5948 txx=mp_make_double(mp, txx,gs_width);
5949 txy=mp_make_double(mp, txy,gs_width);
5950 tyx=mp_make_double(mp, tyx,gs_width);
5951 tyy=mp_make_double(mp, tyy,gs_width);
5954 if ( (txy!=0.0)||(tyx!=0.0)||(txx!=unity)||(tyy!=unity) ) {
5955 if ( (! transformed) ){
5956 mp_ps_print_cmd(mp, "gsave ","q ");
5957 transformed=true;
5961 @ @<Issue \ps\ commands to transform the coordinate system@>=
5962 if ( (txy!=0.0)||(tyx!=0.0) ){
5963 mp_ps_print_ln(mp);
5964 mp_ps_print_char(mp, '[');
5965 mp_ps_pair_out(mp, txx,tyx);
5966 mp_ps_pair_out(mp, txy,tyy);
5967 mp_ps_print(mp, "0 0] concat");
5968 } else if ( (txx!=unity)||(tyy!=unity) ){
5969 mp_ps_print_ln(mp);
5970 mp_ps_pair_out(mp, txx,tyy);
5971 mp_ps_print(mp, "scale");
5974 @ The \ps\ interpreter will probably abort if it encounters a singular
5975 transformation matrix. The determinant must be large enough to ensure that
5976 the printed representation will be nonsingular. Since the printed
5977 representation is always within $2^{-17}$ of the internal |scaled| value, the
5978 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5979 the magnitudes of |txx/65536|, |txy/65536|, etc.
5981 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5982 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5984 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5985 det=mp_take_double(mp, txx,tyy) - mp_take_double(mp, txy,tyx);
5986 d1=4*(aspect_bound+1/65536.0);
5987 if ( fabs(det)<d1 ) {
5988 if ( det>=0 ) { d1=d1-det; s=1; }
5989 else { d1=-d1-det; s=-1; };
5990 d1=d1*unity;
5991 if ( fabs(txx)+fabs(tyy)>=fabs(txy)+fabs(tyy) ) {
5992 if ( fabs(txx)>fabs(tyy) ) tyy=tyy+(d1+s*fabs(txx)) / txx;
5993 else txx=txx+(d1+s*fabs(tyy)) / tyy;
5994 } else {
5995 if ( fabs(txy)>fabs(tyx) ) tyx=tyx+(d1+s*fabs(txy)) / txy;
5996 else txy=txy+(d1+s*fabs(tyx)) / tyx;
6000 @ Here is a simple routine that just fills a cycle.
6002 @<Declarations@>=
6003 static void mp_gr_ps_fill_out (MP mp, mp_gr_knot p);
6005 @ @c
6006 void mp_gr_ps_fill_out (MP mp, mp_gr_knot p) { /* fill cyclic path~|p| */
6007 mp_gr_ps_path_out(mp, p);
6008 mp_ps_print_cmd(mp, " fill"," F");
6009 mp_ps_print_ln(mp);
6012 @ A text node may specify an arbitrary transformation but the usual case
6013 involves only shifting, scaling, and occasionally rotation. The purpose
6014 of |choose_scale| is to select a scale factor so that the remaining
6015 transformation is as ``nice'' as possible. The definition of ``nice''
6016 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
6017 nice because they work out well for bitmap fonts. The code here selects
6018 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
6019 non-shifting part of the transformation matrix. It is careful to avoid
6020 additions that might cause undetected overflow.
6022 @<Declarations@>=
6023 static double mp_gr_choose_scale (MP mp, mp_graphic_object *p) ;
6025 @ @c double mp_gr_choose_scale (MP mp, mp_graphic_object *p) {
6026 /* |p| should point to a text node */
6027 double a,b,c,d,ad,bc; /* temporary values */
6028 double r;
6029 a=gr_txx_val(p);
6030 b=gr_txy_val(p);
6031 c=gr_tyx_val(p);
6032 d=gr_tyy_val(p);
6033 if ( a<0 ) negate(a);
6034 if ( b<0 ) negate(b);
6035 if ( c<0 ) negate(c);
6036 if ( d<0 ) negate(d);
6037 ad=(a-d)/2.0;
6038 bc=(b-c)/2.0;
6039 a = (d+ad);
6040 b = ad;
6041 d = sqrt(a*a + b*b);
6042 a = (c+bc);
6043 b = bc;
6044 c = sqrt(a*a + b*b);
6045 r = sqrt(c*c + d*d);
6046 return r;
6049 @ The potential overflow here is caused by the fact the returned value
6050 has to fit in a |name_type|, which is a quarterword.
6052 @d fscale_tolerance (65/65536.0) /* that's $.001\times2^{16}$ */
6054 @<Declarations@>=
6055 static quarterword mp_size_index (MP mp, font_number f, double s) ;
6057 @ @c
6058 quarterword mp_size_index (MP mp, font_number f, double s) {
6059 mp_node p,q; /* the previous and current font size nodes */
6060 int i; /* the size index for |q| */
6061 p=NULL;
6062 q=mp->font_sizes[f];
6063 i=0;
6064 while ( q!=null ) {
6065 if ( fabs(s-sc_factor(q))<=fscale_tolerance )
6066 return (quarterword)i;
6067 else
6068 { p=q; q=mp_link(q); incr(i); };
6069 if ( i==max_quarterword )
6070 mp_overflow(mp, "sizes per font",max_quarterword);
6071 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
6073 q=(mp_node)mp_xmalloc(mp, 1, font_size_size);
6074 mp_link(q) = NULL;
6075 sc_factor(q)=s;
6076 if ( i==0 ) mp->font_sizes[f]=q; else mp_link(p)=q;
6077 return (quarterword)i;
6080 @ @<Declarations@>=
6081 static double mp_indexed_size (MP mp,font_number f, quarterword j);
6083 @ @c
6084 double mp_indexed_size (MP mp,font_number f, quarterword j) { /* return scaled */
6085 mp_node p; /* a font size node */
6086 int i; /* the size index for |p| */
6087 p=mp->font_sizes[f];
6088 i=0;
6089 if ( p==null ) mp_confusion(mp, "size");
6090 while ( (i!=j) ) {
6091 incr(i);
6092 /* clang: dereference null pointer 'p' */ assert(p);
6093 p=mp_link(p);
6094 if ( p==null ) mp_confusion(mp, "size");
6096 /* clang: dereference null pointer 'p' */ assert(p);
6097 return sc_factor(p);
6100 @ @<Declarations@>=
6101 static void mp_clear_sizes (MP mp) ;
6103 @ @c void mp_clear_sizes (MP mp) {
6104 font_number f; /* the font whose size list is being cleared */
6105 mp_node p; /* current font size nodes */
6106 for (f=null_font+1;f<=mp->last_fnum;f++) {
6107 while ( mp->font_sizes[f]!=null ) {
6108 p=mp->font_sizes[f];
6109 mp->font_sizes[f]=mp_link(p);
6110 mp_xfree(p);
6115 @ There may be many sizes of one font and we need to keep track of the
6116 characters used for each size. This is done by keeping a linked list of
6117 sizes for each font with a counter in each text node giving the appropriate
6118 position in the size list for its font.
6120 @d font_size_size sizeof(struct mp_font_size_node_data) /* size of a font size node */
6122 @<Types...@>=
6123 typedef struct mp_font_size_node_data {
6124 NODE_BODY;
6125 double sc_factor_; /* scaled */
6126 } mp_font_size_node_data;
6127 typedef struct mp_font_size_node_data* mp_font_size_node;
6130 @ @<Declarations@>=
6131 static void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size);
6133 @ @c
6134 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) {
6135 mp_graphic_object * p;
6136 p=h->body;
6137 while ( p!= NULL ) {
6138 if ( gr_type(p)==mp_text_code ) {
6139 if ( gr_font_n(p)!=null_font ) {
6140 if ( gr_size_index(p)==(unsigned char)next_size )
6141 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p),gr_text_l(p));
6144 p=gr_link(p);
6148 @ @<Unmark all marked characters@>=
6149 for (f=null_font+1;f<=mp->last_fnum;f++) {
6150 if ( mp->font_sizes[f]!=null ) {
6151 mp_unmark_font(mp, f);
6152 mp->font_sizes[f]=null;
6156 @ @<Scan all the text nodes and mark the used ...@>=
6157 p=hh->body;
6158 while ( p!=null ) {
6159 if ( gr_type(p)==mp_text_code ) {
6160 f = gr_font_n(p);
6161 if (f!=null_font ) {
6162 switch (prologues) {
6163 case 2:
6164 case 3:
6165 mp->font_sizes[f] = mp_void;
6166 mp_mark_string_chars(mp, f, gr_text_p(p),gr_text_l(p));
6167 if (mp_has_fm_entry(mp,f,NULL) ) {
6168 if (mp->font_enc_name[f]==NULL )
6169 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
6170 mp_xfree(mp->font_ps_name[f]);
6171 mp->font_ps_name[f] = mp_fm_font_name(mp,f);
6173 break;
6174 case 1:
6175 mp->font_sizes[f]=mp_void;
6176 break;
6177 default:
6178 gr_size_index(p)=(unsigned char)mp_size_index(mp, f,mp_gr_choose_scale(mp, p));
6179 if ( gr_size_index(p)==0 )
6180 mp_mark_string_chars(mp, f, gr_text_p(p),gr_text_l(p));
6184 p=gr_link(p);
6189 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
6191 @<Exported function ...@>=
6192 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) ;
6194 @ @c
6195 int mp_gr_ship_out (mp_edge_object *hh, int qprologues, int qprocset,int standalone) {
6196 mp_graphic_object *p;
6197 double ds,scf; /* design size and scale factor for a text node */
6198 font_number f; /* for loops over fonts while (un)marking characters */
6199 boolean transformed; /* is the coordinate system being transformed? */
6200 int prologues, procset;
6201 MP mp = hh->parent;
6202 if (standalone) {
6203 mp->jump_buf = malloc(sizeof(jmp_buf));
6204 if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)))
6205 return 0;
6207 if (mp->history >= mp_fatal_error_stop ) return 1;
6208 if (qprologues<0)
6209 prologues = (int)((unsigned)number_to_scaled (internal_value(mp_prologues))>>16);
6210 else
6211 prologues=qprologues;
6212 if (qprocset<0)
6213 procset = (int)((unsigned)number_to_scaled (internal_value(mp_procset))>>16);
6214 else
6215 procset=qprocset;
6216 mp_open_output_file(mp);
6217 mp_print_initial_comment(mp, hh, prologues);
6218 /* clang: never read: p = hh->body; */
6219 @<Unmark all marked characters@>;
6220 if ( prologues==2 || prologues==3 ) {
6221 mp_reload_encodings(mp);
6223 @<Scan all the text nodes and mark the used characters@>;
6224 if ( prologues==2 || prologues==3 ) {
6225 mp_print_improved_prologue(mp, hh, prologues, procset);
6226 } else {
6227 mp_print_prologue(mp, hh, prologues, procset);
6229 mp_gs_unknown_graphics_state(mp, 0);
6230 p = hh->body;
6231 while ( p!=NULL ) {
6232 if ( gr_has_color(p) ) {
6233 @<Write |pre_script| of |p|@>;
6235 mp_gr_fix_graphics_state(mp, p);
6236 switch (gr_type(p)) {
6237 case mp_fill_code:
6238 if ( gr_pen_p((mp_fill_object *)p)==NULL ) {
6239 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
6240 } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) ) {
6241 mp_gr_stroke_ellipse(mp, p,true);
6242 } else {
6243 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
6244 mp_gr_ps_fill_out(mp, gr_htap_p(p));
6246 if ( gr_post_script((mp_fill_object *)p)!=NULL ) {
6247 mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p));
6248 mp_ps_print_ln(mp);
6250 break;
6251 case mp_stroked_code:
6252 if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) )
6253 mp_gr_stroke_ellipse(mp, p,false);
6254 else {
6255 mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p));
6257 if ( gr_post_script((mp_stroked_object *)p)!=NULL ) {
6258 mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p));
6259 mp_ps_print_ln(mp);
6261 break;
6262 case mp_text_code:
6263 if ( (gr_font_n(p)!=null_font) && (gr_text_l(p)>0) ) {
6264 if ( prologues>0 )
6265 scf=mp_gr_choose_scale(mp, p);
6266 else
6267 scf=mp_indexed_size(mp, gr_font_n(p), (quarterword)gr_size_index(p));
6268 @<Shift or transform as necessary before outputting text node~|p| at scale
6269 factor~|scf|; set |transformed:=true| if the original transformation must
6270 be restored@>;
6271 mp_ps_string_out(mp, gr_text_p(p),gr_text_l(p));
6272 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
6273 @<Print the size information and \ps\ commands for text node~|p|@>;
6274 mp_ps_print_ln(mp);
6276 if ( gr_post_script((mp_text_object *)p)!=NULL ) {
6277 mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp);
6279 break;
6280 case mp_start_clip_code:
6281 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
6282 mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p));
6283 mp_ps_print_cmd(mp, " clip"," W");
6284 mp_ps_print_ln(mp);
6285 if ( number_positive (internal_value(mp_restore_clip_color)) )
6286 mp_gs_unknown_graphics_state(mp, 1);
6287 break;
6288 case mp_stop_clip_code:
6289 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
6290 mp_ps_print_ln(mp);
6291 if ( number_positive (internal_value(mp_restore_clip_color)) )
6292 mp_gs_unknown_graphics_state(mp, 2);
6293 else
6294 mp_gs_unknown_graphics_state(mp, -1);
6295 break;
6296 case mp_start_bounds_code:
6297 case mp_stop_bounds_code:
6298 break;
6299 case mp_special_code:
6301 mp_special_object *ps = (mp_special_object *)p;
6302 mp_ps_print_nl (mp, gr_pre_script(ps));
6303 mp_ps_print_ln (mp);
6305 break;
6306 } /* all cases are enumerated */
6307 p=gr_link(p);
6309 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
6310 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
6311 (mp->close_file)(mp,mp->output_file);
6312 if ( prologues<=0 )
6313 mp_clear_sizes(mp);
6314 return 1;
6317 @ @<Internal Postscript header information@>=
6318 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) ;
6320 @ @c
6321 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) {
6322 return mp_gr_ship_out (hh, prologues, procset, (int)true);
6330 @d do_write_prescript(a,b) {
6331 if ( (gr_pre_script((b *)a))!=NULL ) {
6332 mp_ps_print_nl (mp, gr_pre_script((b *)a));
6333 mp_ps_print_ln(mp);
6337 @<Write |pre_script| of |p|@>=
6339 if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); }
6340 else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); }
6341 else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); }
6344 @ @<Print the size information and \ps\ commands for text node~|p|@>=
6345 ps_room(18);
6346 mp_ps_print_char(mp, ' ');
6347 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
6348 mp_ps_print_double(mp, (mp_take_double(mp, ds,scf)/65536.0));
6349 mp_ps_print(mp, " fshow");
6350 if ( transformed )
6351 mp_ps_print_cmd(mp, " grestore"," Q")
6355 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
6356 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
6357 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
6358 if ( transformed ) {
6359 mp_ps_print_cmd(mp, "gsave [", "q [");
6360 mp_ps_pair_out(mp, mp_make_double(mp, gr_txx_val(p),scf),
6361 mp_make_double(mp, gr_tyx_val(p),scf));
6362 mp_ps_pair_out(mp, mp_make_double(mp, gr_txy_val(p),scf),
6363 mp_make_double(mp, gr_tyy_val(p),scf));
6364 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
6365 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
6366 } else {
6367 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
6368 mp_ps_print_cmd(mp, "moveto","m");
6370 mp_ps_print_ln(mp)
6372 @ @<Internal Postscript header information@>=
6373 void mp_gr_toss_objects ( mp_edge_object *hh) ;
6374 void mp_gr_toss_object (mp_graphic_object *p) ;
6376 @ @c
6377 void mp_gr_toss_object (mp_graphic_object *p) {
6378 mp_fill_object *tf;
6379 mp_stroked_object *ts;
6380 mp_text_object *tt;
6381 switch (gr_type(p)) {
6382 case mp_fill_code:
6383 tf = (mp_fill_object *)p;
6384 mp_xfree(gr_pre_script(tf));
6385 mp_xfree(gr_post_script(tf));
6386 mp_gr_toss_knot_list(mp,gr_pen_p(tf));
6387 mp_gr_toss_knot_list(mp,gr_path_p(tf));
6388 mp_gr_toss_knot_list(mp,gr_htap_p(p));
6389 break;
6390 case mp_stroked_code:
6391 ts = (mp_stroked_object *)p;
6392 mp_xfree(gr_pre_script(ts));
6393 mp_xfree(gr_post_script(ts));
6394 mp_gr_toss_knot_list(mp,gr_pen_p(ts));
6395 mp_gr_toss_knot_list(mp,gr_path_p(ts));
6396 if (gr_dash_p(p)!=NULL)
6397 mp_gr_toss_dashes (mp,gr_dash_p(p));
6398 break;
6399 case mp_text_code:
6400 tt = (mp_text_object *)p;
6401 mp_xfree(gr_pre_script(tt));
6402 mp_xfree(gr_post_script(tt));
6403 mp_xfree(gr_text_p(p));
6404 mp_xfree(gr_font_name(p));
6405 break;
6406 case mp_start_clip_code:
6407 mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p));
6408 break;
6409 case mp_start_bounds_code:
6410 mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p));
6411 break;
6412 case mp_stop_clip_code:
6413 case mp_stop_bounds_code:
6414 break;
6415 case mp_special_code:
6416 mp_xfree(gr_pre_script((mp_special_object *)p));
6417 break;
6418 } /* all cases are enumerated */
6419 mp_xfree(p);
6423 @ @c
6424 void mp_gr_toss_objects (mp_edge_object *hh) {
6425 mp_graphic_object *p, *q;
6426 p = hh->body;
6427 while ( p!=NULL ) {
6428 q = gr_link(p);
6429 mp_gr_toss_object(p);
6430 p=q;
6432 mp_xfree(hh->filename);
6433 mp_xfree(hh);
6436 @ @<Internal Postscript header information@>=
6437 mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ;
6439 @ @c
6440 mp_graphic_object *
6441 mp_gr_copy_object (MP mp, mp_graphic_object *p) {
6442 mp_fill_object *tf;
6443 mp_stroked_object *ts;
6444 mp_text_object *tt;
6445 mp_clip_object *tc;
6446 mp_bounds_object *tb;
6447 mp_special_object *tp;
6448 mp_graphic_object *q = NULL;
6449 switch (gr_type(p)) {
6450 case mp_fill_code:
6451 tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code);
6452 gr_pre_script(tf) = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p));
6453 gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p));
6454 gr_path_p(tf) = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p));
6455 gr_htap_p(tf) = mp_gr_copy_path(mp,gr_htap_p(p));
6456 gr_pen_p(tf) = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p));
6457 q = (mp_graphic_object *)tf;
6458 break;
6459 case mp_stroked_code:
6460 ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code);
6461 gr_pre_script(ts) = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p));
6462 gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p));
6463 gr_path_p(ts) = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p));
6464 gr_pen_p(ts) = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p));
6465 gr_dash_p(ts) = mp_gr_copy_dashes(mp,gr_dash_p(p));
6466 q = (mp_graphic_object *)ts;
6467 break;
6468 case mp_text_code:
6469 tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code);
6470 gr_pre_script(tt) = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p));
6471 gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p));
6472 gr_text_p(tt) = mp_xstrldup(mp, gr_text_p(p), gr_text_l(p));
6473 gr_text_l(tt) = gr_text_l(p);
6474 gr_font_name(tt) = mp_xstrdup(mp, gr_font_name(p));
6475 q = (mp_graphic_object *)tt;
6476 break;
6477 case mp_start_clip_code:
6478 tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code);
6479 gr_path_p(tc) = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p));
6480 q = (mp_graphic_object *)tc;
6481 break;
6482 case mp_start_bounds_code:
6483 tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code);
6484 gr_path_p(tb) = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p));
6485 q = (mp_graphic_object *)tb;
6486 break;
6487 case mp_special_code:
6488 tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code);
6489 gr_pre_script(tp) = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p));
6490 q = (mp_graphic_object *)tp;
6491 break;
6492 case mp_stop_clip_code:
6493 q = mp_new_graphic_object(mp, mp_stop_clip_code);
6494 break;
6495 case mp_stop_bounds_code:
6496 q = mp_new_graphic_object(mp, mp_stop_bounds_code);
6497 break;
6498 } /* all cases are enumerated */
6499 return q;