1 % $Id: mp.web,v 1.8 2005/08/24 10:54:02 taco Exp $
2 % MetaPost, by John Hobby. Public domain.
4 % Much of this program was copied with permission from MF.web Version 1.9
5 % It interprets a language very similar to D.E. Knuth's METAFONT, but with
6 % changes designed to make it more suitable for PostScript output.
8 % TeX is a trademark of the American Mathematical Society.
9 % METAFONT is a trademark of Addison-Wesley Publishing Company.
10 % PostScript is a trademark of Adobe Systems Incorporated.
12 % Here is TeX material that gets inserted after \input webmac
13 \def\hang{\hangindent 3em\noindent\ignorespaces}
14 \def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
17 \def\ph{\hbox{Pascal-H}}
18 \def\psqrt#1{\sqrt{\mathstrut#1}}
20 \def\pct!{{\char`\%}} % percent sign in ordinary text
21 \font\tenlogo=logo10 % font used for the METAFONT logo
23 \def\MF{{\tenlogo META}\-{\tenlogo FONT}}
24 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
25 \def\<#1>{$\langle#1\rangle$}
26 \def\section{\mathhexbox278}
27 \let\swap=\leftrightarrow
28 \def\round{\mathop{\rm round}\nolimits}
29 \mathchardef\vb="026A % synonym for `\|'
30 \def\[#1]{} % from pascal web
31 \def\(#1){} % this is used to make section names sort themselves better
32 \def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
34 \let\?=\relax % we want to be able to \write a \?
36 \def\title{MetaPost \ps\ output}
37 \def\topofcontents{\hsize 5.5in
38 \vglue -30pt plus 1fil minus 1.5in
39 \def\?##1]{\hbox to 1in{\hfil##1.\ }}
41 \def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
50 @d unity 0200000 /* $2^{16}$, represents 1.00000 */
51 @d el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
52 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
53 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
54 @d negate(A) (A)=-(A) /* change the sign of a variable */
57 @d print_err(A) mp_print_err(mp,(A))
58 @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
68 #include "mpmp.h" /* internal header */
69 #include "mppsout.h" /* internal header */
72 @<Static variables in the outer block@>
74 @ There is a small bit of code from the backend that bleads through
75 to the frontend because I do not know how to set up the includes
76 properly. Those are the definitions of |struct libavl_allocator|
77 and |typedef struct psout_data_struct * psout_data|.
79 The |libavl_allocator| is a trick that makes sure that frontends
80 do not need |avl.h|, and the |psout_data| is needed for the backend
85 typedef struct psout_data_struct {
88 @<Exported function headers@>
90 @ @<Exported function headers@>=
91 void mp_backend_initialize (MP mp) ;
92 void mp_backend_free (MP mp) ;
95 @c void mp_backend_initialize (MP mp) {
96 mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
97 @<Set initial values@>;
99 void mp_backend_free (MP mp) {
100 @<Dealloc variables@>;
108 @ Writing to ps files
112 /* the number of characters on the current \ps\ file line */
114 @ @<Set initial values@>=
115 mp->ps->ps_offset = 0;
119 @d wps(A) (mp->write_ascii_file)(mp,mp->ps_file,(A))
123 (mp->write_ascii_file)(mp,mp->ps_file,(char *)ss);
125 @d wps_cr (mp->write_ascii_file)(mp,mp->ps_file,"\n")
126 @d wps_ln(A) { wterm_cr; (mp->write_ascii_file)(mp,mp->ps_file,(A)); }
129 void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
135 void mp_ps_print_char (MP mp, ASCII_code s) { /* prints a single character */
137 wps_cr; mp->ps->ps_offset=0;
139 wps_chr(s); incr(mp->ps->ps_offset);
144 void mp_ps_do_print (MP mp, const char *ss, unsigned int len) { /* prints string |s| */
147 mp_ps_print_char(mp, ss[j]); incr(j);
151 @ Deciding where to break the ps output line.
153 @d ps_room(A) if ( (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) {
154 mp_ps_print_ln(mp); /* optional line break */
158 void mp_ps_print (MP mp, const char *ss) {
160 mp_ps_do_print(mp, ss, strlen(ss));
163 @ The procedure |print_nl| is like |print|, but it makes sure that the
164 string appears at the beginning of a new line.
167 void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */
168 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
172 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
175 void mp_ps_print_the_digs (MP mp, eight_bits k) {
176 /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
178 mp_ps_print_char(mp, '0'+mp->dig[k]);
182 @ The following procedure, which prints out the decimal representation of a
183 given integer |n|, has been written carefully so that it works properly
184 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
185 to negative arguments, since such operations are not implemented consistently
186 by all \PASCAL\ compilers.
189 void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */
190 integer m; /* used to negate |n| in possibly dangerous cases */
191 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
193 mp_ps_print_char(mp, '-');
194 if ( n>-100000000 ) {
197 m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
201 mp->dig[0]=0; incr(n);
206 mp->dig[k]=n % 10; n=n / 10; incr(k);
208 mp_ps_print_the_digs(mp, k);
212 void mp_ps_print_int (MP mp,integer n);
214 @ \MP\ also makes use of a trivial procedure to print two digits. The
215 following subroutine is usually called with a parameter in the range |0<=n<=99|.
218 void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */
220 mp_ps_print_char(mp, '0'+(n / 10));
221 mp_ps_print_char(mp, '0'+(n % 10));
224 @ Conversely, here is a procedure analogous to |print_int|. If the output
225 of this procedure is subsequently read by \MP\ and converted by the
226 |round_decimals| routine above, it turns out that the original value will
227 be reproduced exactly. A decimal point is printed only if the value is
228 not an integer. If there is more than one way to print the result with
229 the optimum number of digits following the decimal point, the closest
230 possible value is given.
232 The invariant relation in the \&{repeat} loop is that a sequence of
233 decimal digits yet to be printed will yield the original number if and only if
234 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
235 We can stop if and only if $f=0$ satisfies this condition; the loop will
236 terminate before $s$ can possibly become zero.
239 void mp_ps_print_scaled (MP mp,scaled s) {
240 scaled delta; /* amount of allowable inaccuracy */
242 mp_ps_print_char(mp, '-');
243 negate(s); /* print the sign, if negative */
245 mp_ps_print_int(mp, s / unity); /* print the integer part */
249 mp_ps_print_char(mp, '.');
252 s=s+0100000-(delta / 2); /* round the final digit */
253 mp_ps_print_char(mp, '0'+(s / unity));
261 @* \[44a] Dealing with font encodings.
263 First, here are a few helpers for parsing files
265 @d check_buf(size, buf_size)
266 if ((unsigned)(size) > (unsigned)(buf_size)) {
268 mp_snprintf(S,128,"buffer overflow: (%d,%d) at file %s, line %d",
269 size,buf_size, __FILE__, __LINE__ );
270 mp_fatal_error(mp,S);
273 @d append_char_to_buf(c, p, buf, buf_size) do {
276 if (c == 13 || c == EOF)
278 if (c != ' ' || (p > buf && p[-1] != 32)) {
279 check_buf(p - buf + 1, (buf_size));
284 @d append_eol(p, buf, buf_size) do {
285 check_buf(p - buf + 2, (buf_size));
286 if (p - buf > 1 && p[-1] != 10)
288 if (p - buf > 2 && p[-2] == 32) {
295 @d remove_eol(p, buf) do {
301 @d skip(p, c) if (*p == c) p++
302 @d strend(s) strchr(s, 0)
303 @d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
308 boolean loaded; /* the encoding has been loaded? */
309 char *file_name; /* encoding file name */
310 char *enc_name; /* encoding true name */
311 integer objnum; /* object number */
313 integer tounicode; /* object number of associated ToUnicode entry */
323 #define ENC_BUF_SIZE 0x1000
324 char enc_line[ENC_BUF_SIZE];
328 @d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file)
329 @d enc_close() (mp->close_file)(mp,mp->ps->enc_file)
332 int enc_getchar(MP mp) {
334 unsigned char abyte=0;
335 void *byte_ptr = &abyte;
336 (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len);
341 static boolean mp_enc_open (MP mp, char *n) {
342 mp->ps->enc_file=(mp->open_file)(mp,n, "r", mp_filetype_encoding);
343 if (mp->ps->enc_file!=NULL)
348 static void mp_enc_getline (MP mp) {
353 print_err("unexpected end of file");
356 p = mp->ps->enc_line;
358 c = enc_getchar (mp);
359 append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
361 append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
362 if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
365 static void mp_load_enc (MP mp, char *enc_name,
366 char **enc_encname, char **glyph_names){
367 char buf[ENC_BUF_SIZE], *p, *r;
370 int save_selector = mp->selector;
371 if (!mp_enc_open (mp,enc_name)) {
372 mp_print (mp,"cannot open encoding file for reading");
375 mp_normalize_selector(mp);
377 mp_print (mp, enc_name);
379 if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
380 remove_eol (r, mp->ps->enc_line);
381 print_err ("invalid encoding vector (a name or `[' missing): `");
382 mp_print(mp,mp->ps->enc_line);
386 while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
387 myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
388 memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
389 *(myname+(r-mp->ps->enc_line-1))=0;
390 *enc_encname = myname;
398 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
401 if (names_count > 256) {
402 print_err ("encoding vector contains more than 256 names");
405 if (mp_xstrcmp (buf, notdef) != 0)
406 glyph_names[names_count] = mp_xstrdup (mp,buf);
409 if (*r != 10 && *r != '%') {
410 if (str_prefix (r, "] def"))
413 remove_eol (r, mp->ps->enc_line);
415 ("invalid encoding vector: a name or `] def' expected: `");
416 mp_print(mp,mp->ps->enc_line);
422 r = mp->ps->enc_line;
427 mp->selector = save_selector;
429 static void mp_read_enc (MP mp, enc_entry * e) {
433 mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
437 @ |write_enc| is used to write either external encoding (given in map file) or
438 internal encoding (read from the font file);
439 the 2nd argument is a pointer to the encoding entry;
442 static void mp_write_enc (MP mp, enc_entry * e) {
447 if (e->objnum != 0) /* the encoding has been written already */
452 mp_ps_print(mp,"\n%%%%BeginResource: encoding ");
453 mp_ps_print(mp, e->enc_name);
454 mp_ps_print_nl(mp, "/");
455 mp_ps_print(mp, e->enc_name);
456 mp_ps_print(mp, " [ ");
457 foffset = strlen(e->file_name)+3;
458 for (i = 0; i < 256; i++) {
460 if (s+1+foffset>=80) {
465 mp_ps_print_char(mp,'/');
466 mp_ps_print(mp, g[i]);
467 mp_ps_print_char(mp,' ');
471 mp_ps_print_nl (mp,"] def\n");
472 mp_ps_print(mp,"%%%%EndResource");
476 @ All encoding entries go into AVL tree for fast search by name.
479 struct avl_table *enc_tree;
481 @ Memory management functions for avl
483 @<Static variables in the outer block@>=
484 static const char notdef[] = ".notdef";
487 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
488 static void avl_xfree (struct libavl_allocator *allocator, void *block);
491 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
493 return malloc (size);
495 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
501 struct libavl_allocator avl_xallocator;
503 @ @<Set initial...@>=
504 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
505 mp->ps->avl_xallocator.libavl_free= avl_xfree;
506 mp->ps->enc_tree = NULL;
509 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
511 return strcmp (((const enc_entry *) pa)->file_name,
512 ((const enc_entry *) pb)->file_name);
514 static enc_entry * mp_add_enc (MP mp, char *s) {
518 if (mp->ps->enc_tree == NULL) {
519 mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
522 p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
523 if (p != NULL) /* encoding already registered */
525 p = mp_xmalloc (mp,1,sizeof (enc_entry));
527 p->file_name = mp_xstrdup (mp,s);
530 p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
531 for (i = 0; i < 256; i++)
532 p->glyph_names[i] = (char *) notdef;
533 aa = avl_probe (mp->ps->enc_tree, p);
540 static void mp_destroy_enc_entry (void *pa, void *pb) {
543 p = (enc_entry *) pa;
545 mp_xfree (p->file_name);
546 if (p->glyph_names != NULL)
547 for (i = 0; i < 256; i++)
548 if (p->glyph_names[i] != notdef)
549 mp_xfree (p->glyph_names[i]);
550 mp_xfree (p->glyph_names);
555 static void enc_free (MP mp);
557 @ @c static void enc_free (MP mp) {
558 if (mp->ps->enc_tree != NULL)
559 avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
562 @ @<Exported function headers@>=
563 void mp_reload_encodings (MP mp) ;
566 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) ;
568 @ @c void mp_reload_encodings (MP mp) {
572 int lastfnum = mp->last_fnum;
573 for (f=null_font+1;f<=lastfnum;f++) {
574 if (mp->font_enc_name[f]!=NULL ) {
575 mp_xfree(mp->font_enc_name[f]);
576 mp->font_enc_name[f]=NULL;
578 if (mp_has_fm_entry (mp,f,&fm_cur)) {
579 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
580 e = fm_cur->encoding;
586 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) {
590 for (f=null_font+1;f<=lastfnum;f++) {
591 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
592 if (fm != NULL && (fm->ps_name != NULL)) {
593 if (is_reencoded (fm)) {
594 if (encodings_only || (!is_subsetted (fm))) {
596 mp_write_enc (mp, e);
597 /* clear for next run */
606 @* \[44b] Parsing font map files.
614 @d fm_close() (mp->close_file)(mp,mp->ps->fm_file)
615 @d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file)
618 int fm_getchar(MP mp) {
620 unsigned char abyte=0;
621 void *byte_ptr = &abyte;
622 (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
630 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
631 enum _ltype { MAPFILE, MAPLINE };
632 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
633 typedef struct mitem {
634 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
635 int type; /* map file or map line */
636 char *map_line; /* pointer to map file name or map line */
637 int lineno; /* line number in map file */
643 fm_entry *loaded_tfm_found;
644 fm_entry *avail_tfm_found;
645 fm_entry *non_tfm_found;
646 fm_entry *not_avail_tfm_found;
648 @ @<Set initial...@>=
649 mp->ps->mitem = NULL;
652 static const char nontfm[] = "<nontfm>";
655 @d read_field(r, q, buf) do {
657 while (*r != ' ' && *r != '\0')
665 fm->F = mp_xstrdup(mp,buf);
677 static fm_entry *new_fm_entry (MP mp) {
679 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
684 fm->subset_tag = NULL;
686 fm->tfm_num = null_font;
687 fm->tfm_avail = TFM_UNCHECKED;
695 fm->all_glyphs = false;
702 static void delete_fm_entry (fm_entry * fm) {
703 mp_xfree (fm->tfm_name);
704 mp_xfree (fm->ps_name);
705 mp_xfree (fm->ff_name);
706 mp_xfree (fm->subset_tag);
707 mp_xfree (fm->charset);
711 static ff_entry *new_ff_entry (MP mp) {
713 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
719 static void delete_ff_entry (ff_entry * ff) {
720 mp_xfree (ff->ff_name);
721 mp_xfree (ff->ff_path);
725 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
726 static char buf[SMALL_BUF_SIZE];
727 char *p = tfmname, *r = strend (p) - 1, *q = r;
728 while (q > p && isdigit (*q))
730 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
732 check_buf (q - p + 1, SMALL_BUF_SIZE);
733 strncpy (buf, p, (size_t) (q - p));
739 @ @<Exported function headers@>=
740 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
743 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
744 fm_entry *res = NULL;
745 res = mp_fm_lookup (mp, f);
749 return (res != NULL);
753 struct avl_table *tfm_tree;
754 struct avl_table *ps_tree;
755 struct avl_table *ff_tree;
757 @ @<Set initial...@>=
758 mp->ps->tfm_tree = NULL;
759 mp->ps->ps_tree = NULL;
760 mp->ps->ff_tree = NULL;
762 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
765 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
767 return strcmp (((const fm_entry *) pa)->tfm_name,
768 ((const fm_entry *) pb)->tfm_name);
771 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
773 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
775 const fm_entry *p1 = (const fm_entry *) pa;
776 const fm_entry *p2 = (const fm_entry *) pb;
778 assert (p1->ps_name != NULL && p2->ps_name != NULL);
779 if ((i = strcmp (p1->ps_name, p2->ps_name)))
781 cmp_return (p1->slant, p2->slant);
782 cmp_return (p1->extend, p2->extend);
783 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
784 (i = strcmp (p1->tfm_name, p2->tfm_name)))
789 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
791 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
793 return strcmp (((const ff_entry *) pa)->ff_name,
794 ((const ff_entry *) pb)->ff_name);
797 @ @c static void create_avl_trees (MP mp) {
798 if (mp->ps->tfm_tree == NULL) {
799 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
800 assert (mp->ps->tfm_tree != NULL);
802 if (mp->ps->ps_tree == NULL) {
803 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
804 assert (mp->ps->ps_tree != NULL);
806 if (mp->ps->ff_tree == NULL) {
807 mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
808 assert (mp->ps->ff_tree != NULL);
812 @ The function |avl_do_entry| is not completely symmetrical with regards
813 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
814 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
815 with the original version.
819 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
820 @d set_pslink(fm) ((fm)->links |= LINK_PS)
821 @d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
822 @d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
823 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
824 @d has_pslink(fm) ((fm)->links & LINK_PS)
827 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
833 /* handle |tfm_name| link */
835 if (strcmp (fp->tfm_name, nontfm)) {
836 p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
838 if (mode == FM_DUPIGNORE) {
839 mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
843 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
844 if (mp_has_font_size(mp,p->tfm_num)) {
846 "fontmap entry for `%s' has been used, replace/delete not allowed",
851 a = avl_delete (mp->ps->tfm_tree, p);
858 if (mode != FM_DELETE) {
859 aa = avl_probe (mp->ps->tfm_tree, fp);
865 /* handle |ps_name| link */
867 if (fp->ps_name != NULL) {
868 assert (fp->tfm_name != NULL);
869 p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
871 if (mode == FM_DUPIGNORE) {
873 "ps_name entry for `%s' already exists, duplicates ignored",
877 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
878 if (mp_has_font_size(mp,p->tfm_num)) {
879 /* REPLACE/DELETE not allowed */
881 "fontmap entry for `%s' has been used, replace/delete not allowed",
886 a = avl_delete (mp->ps->ps_tree, p);
889 if (!has_tfmlink (p))
893 if (mode != FM_DELETE) {
894 aa = avl_probe (mp->ps->ps_tree, fp);
900 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
901 return 1; /* deallocation of |fm_entry| structure required */
906 @ consistency check for map entry, with warn flag
909 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
913 if (fm->ps_name != NULL) {
914 if (is_basefont (fm)) {
915 if (is_fontfile (fm) && !is_included (fm)) {
917 mp_snprintf(s,128, "invalid entry for `%s': "
918 "font file must be included or omitted for base fonts",
924 } else { /* not a base font */
925 /* if no font file given, drop this entry */
926 /* |if (!is_fontfile (fm)) {
929 "invalid entry for `%s': font file missing",
938 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
941 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
947 if ((fm->slant != 0 || fm->extend != 0) &&
948 (is_truetype (fm))) {
951 "invalid entry for `%s': "
952 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
958 if (abs (fm->slant) > 1000) {
961 "invalid entry for `%s': too big value of SlantFont (%g)",
962 fm->tfm_name, fm->slant / 1000.0);
967 if (abs (fm->extend) > 2000) {
970 "invalid entry for `%s': too big value of ExtendFont (%g)",
971 fm->tfm_name, fm->extend / 1000.0);
977 !(is_truetype (fm) && is_included (fm) &&
978 is_subsetted (fm) && !is_reencoded (fm))) {
981 "invalid entry for `%s': "
982 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
991 @ returns true if s is one of the 14 std. font names; speed-trimmed.
993 @c static boolean check_basefont (char *s) {
994 static const char *basefont_names[] = {
996 "Courier-Bold", /* 1:12 */
997 "Courier-Oblique", /* 2:15 */
998 "Courier-BoldOblique", /* 3:19 */
999 "Helvetica", /* 4:9 */
1000 "Helvetica-Bold", /* 5:14 */
1001 "Helvetica-Oblique", /* 6:17 */
1002 "Helvetica-BoldOblique", /* 7:21 */
1004 "Times-Roman", /* 9:11 */
1005 "Times-Bold", /* 10:10 */
1006 "Times-Italic", /* 11:12 */
1007 "Times-BoldItalic", /* 12:16 */
1008 "ZapfDingbats" /* 13:12 */
1010 static const int Index[] =
1011 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1014 const size_t n = strlen (s);
1018 if (n == 12) { /* three names have length 12 */
1021 k = 1; /* Courier-Bold */
1024 k = 11; /* Times-Italic */
1027 k = 13; /* ZapfDingbats */
1034 if (k > -1 && !strcmp (basefont_names[k], s))
1040 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1042 @c static void fm_scan_line (MP mp) {
1043 int a, b, c, j, u = 0, v = 0;
1046 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1047 char *p, *q, *r, *s;
1049 switch (mp->ps->mitem->type) {
1053 c = fm_getchar (mp);
1054 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1060 r = mp->ps->mitem->map_line;
1065 if (*r == '\0' || is_cfg_comment (*r))
1067 fm = new_fm_entry (mp);
1068 read_field (r, q, buf);
1069 set_field (tfm_name);
1071 read_field (r, q, buf);
1072 if (*buf != '<' && *buf != '"')
1073 set_field (ps_name);
1075 r = p; /* unget the field */
1076 if (isdigit (*r)) { /* font flags given */
1077 fm->flags = atoi (r);
1078 while (isdigit (*r))
1081 while (1) { /* loop through "specials", encoding, font file */
1086 case '"': /* opening quote */
1091 if (sscanf (r, "%f %n", &d, &j) > 0) {
1092 s = r + j; /* jump behind number, eat also blanks, if any */
1093 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1094 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1095 if (str_prefix (s, "SlantFont")) {
1096 d *= 1000.0; /* correct rounding also for neg. numbers */
1097 fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1098 r = s + strlen ("SlantFont");
1099 } else if (str_prefix (s, "ExtendFont")) {
1101 fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1102 if (fm->extend == 1000)
1104 r = s + strlen ("ExtendFont");
1105 } else { /* unknown name */
1107 *r != ' ' && *r != '"' && *r != '\0';
1108 r++); /* jump over name */
1109 c = *r; /* remember char for temporary end of string */
1111 mp_snprintf(warn_s,128,
1112 "invalid entry for `%s': unknown name `%s' ignored",
1118 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1121 if (*r == '"') /* closing quote */
1124 mp_snprintf(warn_s,128,
1125 "invalid entry for `%s': closing quote missing",
1131 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1132 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1138 default: /* encoding or font file specification */
1142 if (*r == '<' || *r == '[')
1145 read_field (r, q, buf);
1146 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1147 if (strlen (buf) > 4 && strcasecmp (strend (buf) - 4, ".enc") == 0) {
1148 fm->encoding = mp_add_enc (mp, buf);
1149 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1150 } else if (strlen (buf) > 0) { /* file name given */
1151 /* font file, formats:
1152 * subsetting: '<cmr10.pfa'
1153 * no subsetting: '<<cmr10.pfa'
1154 * no embedding: 'cmr10.pfa'
1156 if (a == '<' || u == '<') {
1158 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1160 /* otherwise b == '<' (or '[') => no subsetting */
1162 set_field (ff_name);
1171 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1173 if (is_fontfile (fm)
1174 && strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1176 if (check_fm_entry (mp,fm, true) != 0)
1179 Until here the map line has been completely scanned without errors;
1180 fm points to a valid, freshly filled-out |fm_entry| structure.
1181 Now follows the actual work of registering/deleting.
1183 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
1186 delete_fm_entry (fm);
1190 @c static void fm_read_info (MP mp) {
1193 if (mp->ps->tfm_tree == NULL)
1194 create_avl_trees (mp);
1195 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1197 mp->ps->mitem->lineno = 1;
1198 switch (mp->ps->mitem->type) {
1200 n = mp->ps->mitem->map_line;
1201 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1202 if (!mp->ps->fm_file) {
1203 mp_snprintf(s,256,"cannot open font map file %s",n);
1206 int save_selector = mp->selector;
1207 mp_normalize_selector(mp);
1210 while (!fm_eof ()) {
1212 mp->ps->mitem->lineno++;
1216 mp->selector = save_selector;
1217 mp->ps->fm_file = NULL;
1227 mp->ps->mitem->map_line = NULL; /* done with this line */
1231 @ @c static void init_fm (fm_entry * fm, font_number f) {
1232 if (fm->tfm_num == null_font ) {
1234 fm->tfm_avail = TFM_FOUND;
1239 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1242 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1247 if (mp->ps->tfm_tree == NULL)
1248 fm_read_info (mp); /* only to read default map file */
1249 tfm = mp->font_name[f];
1250 assert (strcmp (tfm, nontfm));
1251 /* Look up for full <tfmname>[+-]<expand> */
1253 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1256 return (fm_entry *) fm;
1258 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1259 if (tfm == NULL) /* not an expanded font, nothing to do */
1263 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1264 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1265 return (fm_entry *) fm; /* font expansion uses the base font */
1270 @ Early check whether a font file exists. Used e. g. for replacing fonts
1271 of embedded PDF files: Without font file, the font within the embedded
1272 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1273 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1274 only once per font file name + expansion parameter. This might help
1275 keeping speed, if many PDF pages with same fonts are to be embedded.
1277 The |ff_tree| contains only font files, which are actually needed,
1278 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1281 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1286 assert (fm->ff_name != NULL);
1287 tmp.ff_name = fm->ff_name;
1288 ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1289 if (ff == NULL) { /* not yet in database */
1290 ff = new_ff_entry (mp);
1291 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1292 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1293 aa = avl_probe (mp->ps->ff_tree, ff);
1294 assert (aa != NULL);
1299 @ Process map file given by its name or map line contents. Items not
1300 beginning with [+-=] flush default map file, if it has not yet been
1301 read. Leading blanks and blanks immediately following [+-=] are ignored.
1304 @c static void mp_process_map_item (MP mp, char *s, int type) {
1308 s++; /* ignore leading blank */
1310 case '+': /* +mapfile.map, +mapline */
1311 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1314 case '=': /* =mapfile.map, =mapline */
1315 mode = FM_REPLACE; /* try to replace earlier entry */
1318 case '-': /* -mapfile.map, -mapline */
1319 mode = FM_DELETE; /* try to delete entry */
1323 mode = FM_DUPIGNORE; /* like +, but also: */
1324 mp_xfree(mp->ps->mitem->map_line);
1325 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1328 s++; /* ignore blank after [+-=] */
1329 p = s; /* map item starts here */
1331 case MAPFILE: /* remove blank at end */
1332 while (*p != '\0' && *p != ' ')
1336 case MAPLINE: /* blank at end allowed */
1341 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1343 if (*s != '\0') { /* only if real item to process */
1344 mp->ps->mitem->mode = mode;
1345 mp->ps->mitem->type = type;
1346 mp->ps->mitem->map_line = s;
1351 @ @<Exported function headers@>=
1352 void mp_map_file (MP mp, str_number t);
1353 void mp_map_line (MP mp, str_number t);
1354 void mp_init_map_file (MP mp, int is_troff);
1357 void mp_map_file (MP mp, str_number t) {
1358 char *s = mp_xstrdup(mp,mp_str (mp,t));
1359 mp_process_map_item (mp, s, MAPFILE);
1362 void mp_map_line (MP mp, str_number t) {
1363 char *s = mp_xstrdup(mp,mp_str (mp,t));
1364 mp_process_map_item (mp, s, MAPLINE);
1369 @c void mp_init_map_file (MP mp, int is_troff) {
1371 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1372 mp->ps->mitem->mode = FM_DUPIGNORE;
1373 mp->ps->mitem->type = MAPFILE;
1374 mp->ps->mitem->map_line = NULL;
1375 r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap);
1378 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1381 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1383 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1388 @ @<Dealloc variables@>=
1389 if (mp->ps->mitem!=NULL) {
1390 mp_xfree(mp->ps->mitem->map_line);
1391 mp_xfree(mp->ps->mitem);
1397 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1400 fm = (fm_entry *) pa;
1401 if (!has_pslink (fm))
1402 delete_fm_entry (fm);
1406 static void destroy_fm_entry_ps (void *pa, void *pb) {
1409 fm = (fm_entry *) pa;
1410 if (!has_tfmlink (fm))
1411 delete_fm_entry (fm);
1415 static void destroy_ff_entry (void *pa, void *pb) {
1418 ff = (ff_entry *) pa;
1419 delete_ff_entry (ff);
1423 static void fm_free (MP mp);
1426 static void fm_free (MP mp) {
1427 if (mp->ps->tfm_tree != NULL)
1428 avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1429 if (mp->ps->ps_tree != NULL)
1430 avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1431 if (mp->ps->ff_tree != NULL)
1432 avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1435 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1436 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1437 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
1438 one or more spaces, a PostScript name, and possibly a space and some other junk.
1439 This routine reads the table, updates |font_ps_name| entries starting after
1440 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
1442 @d ps_tab_name "psfonts.map" /* locates font name translation table */
1445 static void mp_read_psname_table (MP mp) ;
1448 static void mp_read_psname_table (MP mp) {
1450 if (mp->ps->mitem == NULL) {
1451 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1452 mp->ps->mitem->mode = FM_DUPIGNORE;
1453 mp->ps->mitem->type = MAPFILE;
1454 mp->ps->mitem->map_line = NULL;
1456 mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name);
1458 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1459 if (mp_has_fm_entry(mp, k, NULL)) {
1460 mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1463 mp->last_ps_fnum=mp->last_fnum;
1467 @ The traditional function is a lot shorter now.
1471 @* \[44c] Helper functions for Type1 fonts.
1474 typedef char char_entry;
1475 typedef unsigned char Byte;
1479 char_entry *char_ptr, *char_array;
1481 char *job_id_string;
1483 @ @<Set initial...@>=
1484 mp->ps->char_array = NULL;
1485 mp->ps->job_id_string = NULL;
1488 @d SMALL_ARRAY_SIZE 256
1492 void mp_set_job_id (MP mp) {
1493 char *name_string, *format_string, *s;
1495 if (mp->ps->job_id_string != NULL)
1497 if ( mp->job_name==NULL )
1498 mp->job_name = mp_xstrdup(mp,"mpout");
1499 name_string = mp_xstrdup (mp,mp->job_name);
1500 format_string = mp_xstrdup (mp,mp->mem_ident);
1501 slen = SMALL_BUF_SIZE +
1502 strlen (name_string) +
1503 strlen (format_string);
1504 s = mp_xmalloc (mp,slen, sizeof (char));
1505 sprintf (s,"%.4d/%.2d/%.2d %.2d:%.2d %s %s",
1506 (mp->internal[mp_year]>>16),
1507 (mp->internal[mp_month]>>16),
1508 (mp->internal[mp_day]>>16),
1509 (mp->internal[mp_time]>>16) / 60,
1510 (mp->internal[mp_time]>>16) % 60,
1511 name_string, format_string);
1512 mp->ps->job_id_string = mp_xstrdup (mp,s);
1514 mp_xfree (name_string);
1515 mp_xfree (format_string);
1517 static void fnstr_append (MP mp, const char *s) {
1518 size_t l = strlen (s) + 1;
1519 alloc_array (char, l, SMALL_ARRAY_SIZE);
1520 strcat (mp->ps->char_ptr, s);
1521 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1524 @ @<Exported function headers@>=
1525 void mp_set_job_id (MP mp) ;
1527 @ @<Dealloc variables@>=
1528 mp_xfree(mp->ps->job_id_string);
1530 @ this is not really a true crc32, but it should be just enough to keep
1531 subsets prefixes somewhat disjunct
1534 static unsigned long crc32 (int oldcrc, const Byte *buf, int len) {
1535 unsigned long ret = 0;
1538 ret = (23<<24)+(45<<16)+(67<<8)+89;
1541 ret = (ret<<2)+buf[i];
1544 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1545 integer b; /* |char_base[f]| */
1547 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1553 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, int tex_font)
1559 if (mp->ps->job_id_string ==NULL)
1560 mp_fatal_error(mp, "no job id!");
1561 l = strlen (mp->ps->job_id_string) + 1;
1563 alloc_array (char, l, SMALL_ARRAY_SIZE);
1564 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1565 mp->ps->char_ptr = strend (mp->ps->char_array);
1566 if (fm_cur->tfm_name != NULL) {
1567 fnstr_append (mp," TFM name: ");
1568 fnstr_append (mp,fm_cur->tfm_name);
1570 fnstr_append (mp," PS name: ");
1571 if (fm_cur->ps_name != NULL)
1572 fnstr_append (mp,fm_cur->ps_name);
1573 fnstr_append (mp," Encoding: ");
1574 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1575 fnstr_append (mp,(fm_cur->encoding)->file_name);
1577 fnstr_append (mp,"built-in");
1578 fnstr_append (mp," CharSet: ");
1579 for (i = 0; i < 256; i++)
1580 if (mp_char_marked (mp,tex_font, i) && glyph_names[i] != notdef) {
1581 if (glyph_names[i]!=NULL) {
1582 fnstr_append (mp,"/");
1583 fnstr_append (mp,glyph_names[i]);
1586 if (fm_cur->charset != NULL) {
1587 fnstr_append (mp," Extra CharSet: ");
1588 fnstr_append (mp, fm_cur->charset);
1590 crc = crc32 (0L, Z_NULL, 0);
1591 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1592 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1593 * there are 26 uppercase chars ==> each char represents a number in range
1594 * |0..25|. The maximal number that can be represented by the tag is
1595 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1596 * of the CRC must be dropped out.
1598 for (i = 0; i < 6; i++) {
1599 tag[i] = 'A' + crc % 26;
1603 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1609 @d external_enc() (fm_cur->encoding)->glyph_names
1610 @d is_used_char(c) mp_char_marked (mp, tex_font, c)
1611 @d end_last_eexec_line()
1612 mp->ps->hexline_length = HEXLINE_WIDTH;
1614 mp->ps->t1_eexec_encrypt = false
1615 @d t1_log(s) mp_print(mp,(char *)s)
1616 @d t1_putchar(c) wps_chr(c)
1617 @d embed_all_glyphs(tex_font) false
1619 @d extra_charset() mp->ps->dvips_extra_charset
1620 @d update_subset_tag()
1621 @d fixedcontent true
1624 #define PRINTF_BUF_SIZE 1024
1625 char *dvips_extra_charset;
1627 unsigned char *grid;
1628 char *ext_glyph_names[256];
1629 char print_buf[PRINTF_BUF_SIZE];
1630 int t1_byte_waiting;
1632 @ @<Set initial ...@>=
1633 mp->ps->dvips_extra_charset=NULL;
1634 mp->ps->t1_byte_waiting=0;
1637 @d t1_ungetchar(A) mp->ps->t1_byte_waiting=A
1638 @d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file)
1639 @d t1_close() (mp->close_file)(mp,mp->ps->t1_file)
1640 @d valid_code(c) (c >= 0 && c < 256)
1643 int t1_getchar (MP mp) {
1645 unsigned char abyte=0;
1646 void *byte_ptr = &abyte;
1647 if (mp->ps->t1_byte_waiting) {
1648 abyte = mp->ps->t1_byte_waiting;
1649 mp->ps->t1_byte_waiting = 0;
1651 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
1656 @ @<Static variables in the outer block@>=
1657 static const char *standard_glyph_names[256] =
1658 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1659 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1660 notdef, notdef, notdef, notdef, notdef, notdef,
1661 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1662 "space", "exclam", "quotedbl", "numbersign",
1663 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1664 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1665 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1666 "eight", "nine", "colon", "semicolon", "less",
1667 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1668 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1669 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1670 "backslash", "bracketright", "asciicircum", "underscore",
1671 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1672 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1673 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1674 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1675 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1676 notdef, notdef, notdef, notdef, notdef, notdef,
1677 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1678 notdef, notdef, notdef, "exclamdown", "cent",
1679 "sterling", "fraction", "yen", "florin", "section", "currency",
1680 "quotesingle", "quotedblleft", "guillemotleft",
1681 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1682 "dagger", "daggerdbl", "periodcentered", notdef,
1683 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1684 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1685 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1686 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1687 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1688 notdef, notdef, notdef, notdef, notdef, notdef,
1689 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1690 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1691 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1692 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1693 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1694 "germandbls", notdef, notdef, notdef, notdef };
1695 static const char charstringname[] = "/CharStrings";
1698 char **t1_glyph_names;
1699 char *t1_builtin_glyph_names[256];
1700 char charsetstr[0x4000];
1701 boolean read_encoding_only;
1705 #define T1_BUF_SIZE 0x10
1709 #define CS_VMOVETO 4
1710 #define CS_RLINETO 5
1711 #define CS_HLINETO 6
1712 #define CS_VLINETO 7
1713 #define CS_RRCURVETO 8
1714 #define CS_CLOSEPATH 9
1715 #define CS_CALLSUBR 10
1716 #define CS_RETURN 11
1717 #define CS_ESCAPE 12
1719 #define CS_ENDCHAR 14
1720 #define CS_RMOVETO 21
1721 #define CS_HMOVETO 22
1722 #define CS_VHCURVETO 30
1723 #define CS_HVCURVETO 31
1724 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1726 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1727 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1728 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1729 #define CS_SEAC CS_1BYTE_MAX + 6
1730 #define CS_SBW CS_1BYTE_MAX + 7
1731 #define CS_DIV CS_1BYTE_MAX + 12
1732 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1733 #define CS_POP CS_1BYTE_MAX + 17
1734 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1735 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1736 #define CS_MAX CS_2BYTE_MAX
1739 typedef unsigned char byte;
1741 byte nargs; /* number of arguments */
1742 boolean bottom; /* take arguments from bottom of stack? */
1743 boolean clear; /* clear stack? */
1745 } cc_entry; /* CharString Command */
1747 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1749 unsigned short len; /* length of the whole string */
1750 unsigned short cslen; /* length of the encoded part of the string */
1756 unsigned short t1_dr, t1_er;
1757 unsigned short t1_c1, t1_c2;
1758 unsigned short t1_cslen;
1761 @ @<Set initial...@>=
1762 mp->ps->t1_c1 = 52845;
1763 mp->ps->t1_c2 = 22719;
1766 typedef char t1_line_entry;
1767 typedef char t1_buf_entry;
1770 t1_line_entry *t1_line_ptr, *t1_line_array;
1771 size_t t1_line_limit;
1772 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1773 size_t t1_buf_limit;
1775 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1776 char *cs_dict_start, *cs_dict_end;
1777 int cs_count, cs_size, cs_size_pos;
1779 char *subr_array_start, *subr_array_end;
1780 int subr_max, subr_size, subr_size_pos;
1782 @ @<Set initial...@>=
1783 mp->ps->t1_line_array = NULL;
1784 mp->ps->t1_buf_array = NULL;
1787 This list contains the begin/end tokens commonly used in the
1788 /Subrs array of a Type 1 font.
1790 @<Static variables in the outer block@>=
1791 static const char *cs_token_pairs_list[][2] = {
1794 {" RD", "noaccess put"},
1795 {" -|", "noaccess put"},
1800 const char **cs_token_pair;
1801 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1802 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1803 long t1_block_length;
1811 @<Set initial ...@>=
1812 mp->ps->hexline_length = 0;
1815 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1816 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1817 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1818 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1819 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1820 @d t1_subrs() t1_prefix("/Subrs")
1821 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1822 @d t1_cleartomark() t1_prefix("cleartomark")
1824 @d isdigit(A) ((A)>='0'&&(A)<='9')
1827 static void end_hexline (MP mp) {
1828 if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
1830 mp->ps->hexline_length = 0;
1833 static void t1_check_pfa (MP mp) {
1834 const int c = t1_getchar (mp);
1835 mp->ps->t1_pfa = (c != 128) ? true : false;
1838 static int t1_getbyte (MP mp)
1840 int c = t1_getchar (mp);
1843 if (mp->ps->t1_block_length == 0) {
1845 mp_fatal_error (mp, "invalid marker");
1846 c = t1_getchar (mp);
1852 mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
1853 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 8;
1854 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 16;
1855 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 24;
1856 c = t1_getchar (mp);
1858 mp->ps->t1_block_length--;
1861 static int hexval (int c) {
1862 if (c >= 'A' && c <= 'F')
1863 return c - 'A' + 10;
1864 else if (c >= 'a' && c <= 'f')
1865 return c - 'a' + 10;
1866 else if (c >= '0' && c <= '9')
1871 static byte edecrypt (MP mp, byte cipher) {
1873 if (mp->ps->t1_pfa) {
1874 while (cipher == 10 || cipher == 13)
1875 cipher = t1_getbyte (mp);
1876 mp->ps->last_hexbyte = cipher = (hexval (cipher) << 4) + hexval (t1_getbyte (mp));
1878 plain = (cipher ^ (mp->ps->t1_dr >> 8));
1879 mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1882 static byte cdecrypt (MP mp, byte cipher, unsigned short *cr)
1884 const byte plain = (cipher ^ (*cr >> 8));
1885 *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1888 static byte eencrypt (MP mp, byte plain)
1890 const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1891 mp->ps->t1_er = (cipher + mp->ps->t1_er) * mp->ps->t1_c1 + mp->ps->t1_c2;
1895 static byte cencrypt (MP mp, byte plain, unsigned short *cr)
1897 const byte cipher = (plain ^ (*cr >> 8));
1898 *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1902 static char *eol (char *s) {
1903 char *p = strend (s);
1904 if (p - s > 1 && p[-1] != 10) {
1910 static float t1_scan_num (MP mp, char *p, char **r)
1915 if (sscanf (p, "%g", &f) != 1) {
1916 remove_eol (p, mp->ps->t1_line_array);
1917 mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1918 mp_fatal_error(mp,s);
1921 for (; isdigit (*p) || *p == '.' ||
1922 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1928 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1931 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1934 while (s1 >= begin_buf && s2 >= s) {
1943 @d alloc_array(T, n, s) do {
1944 if (mp->ps->T##_array == NULL) {
1945 mp->ps->T##_limit = (s);
1946 if ((unsigned)(n) > mp->ps->T##_limit)
1947 mp->ps->T##_limit = (n);
1948 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
1949 mp->ps->T##_ptr = mp->ps->T##_array;
1951 else if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
1952 size_t last_ptr_index;
1953 last_ptr_index = mp->ps->T##_ptr - mp->ps->T##_array;
1954 mp->ps->T##_limit *= 2;
1955 if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
1956 mp->ps->T##_limit = mp->ps->T##_ptr - mp->ps->T##_array + (n);
1957 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
1958 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
1962 @d out_eexec_char(A) t1_outhex(mp,(A))
1965 static void t1_outhex (MP mp, byte b)
1967 static const char *hexdigits = "0123456789ABCDEF";
1968 t1_putchar (hexdigits[b / 16]);
1969 t1_putchar (hexdigits[b % 16]);
1970 mp->ps->hexline_length += 2;
1973 static void t1_getline (MP mp) {
1974 int c, l, eexec_scan;
1976 static const char eexec_str[] = "currentfile eexec";
1977 static int eexec_len = 17; /* |strlen(eexec_str)| */
1980 mp_fatal_error (mp,"unexpected end of file");
1981 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
1982 alloc_array (t1_line, 1, T1_BUF_SIZE);
1983 mp->ps->t1_cslen = 0;
1985 c = t1_getbyte (mp);
1988 while (!t1_eof ()) {
1989 if (mp->ps->t1_in_eexec == 1)
1990 c = edecrypt (mp,c);
1991 alloc_array (t1_line, 1, T1_BUF_SIZE);
1992 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
1993 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
1994 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
1999 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2001 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
2002 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2003 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2004 p = mp->ps->t1_line_ptr - 5;
2007 mp->ps->t1_cslen = l = t1_scan_num (mp, p + 1, 0);
2008 mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2009 /* |mp->ps->cs_start| is an index now */
2010 alloc_array (t1_line, l, T1_BUF_SIZE);
2012 *mp->ps->t1_line_ptr++ = edecrypt (mp,t1_getbyte (mp));
2014 c = t1_getbyte (mp);
2016 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
2017 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2018 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2020 if (eexec_scan == eexec_len)
2021 mp->ps->t1_in_eexec = 1;
2023 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2024 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2025 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2028 static void t1_putline (MP mp)
2030 char *p = mp->ps->t1_line_array;
2031 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2033 if (mp->ps->t1_eexec_encrypt) {
2034 while (p < mp->ps->t1_line_ptr)
2035 out_eexec_char (eencrypt (mp,*p++));
2037 while (p < mp->ps->t1_line_ptr)
2042 static void t1_puts (MP mp, const char *s)
2044 if (s != mp->ps->t1_line_array)
2045 strcpy (mp->ps->t1_line_array, s);
2046 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2050 static void t1_printf (MP mp, const char *fmt, ...)
2053 va_start (args, fmt);
2054 vsprintf (mp->ps->t1_line_array, fmt, args);
2055 t1_puts (mp,mp->ps->t1_line_array);
2059 static void t1_init_params (MP mp, const char *open_name_prefix,
2060 char *cur_file_name) {
2061 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2062 t1_log (open_name_prefix);
2063 t1_log (cur_file_name);
2065 mp->ps->t1_lenIV = 4;
2066 mp->ps->t1_dr = 55665;
2067 mp->ps->t1_er = 55665;
2068 mp->ps->t1_in_eexec = 0;
2069 mp->ps->t1_cs = false;
2070 mp->ps->t1_scan = true;
2071 mp->ps->t1_synthetic = false;
2072 mp->ps->t1_eexec_encrypt = false;
2073 mp->ps->t1_block_length = 0;
2076 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2077 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2078 t1_log (close_name_suffix);
2083 static void t1_check_block_len (MP mp, boolean decrypt) {
2086 if (mp->ps->t1_block_length == 0)
2088 c = t1_getbyte (mp);
2090 c = edecrypt (mp,c);
2091 l = mp->ps->t1_block_length;
2092 if (!(l == 0 && (c == 10 || c == 13))) {
2093 mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2099 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2101 if (!mp->ps->t1_pfa)
2102 t1_check_block_len (mp, false);
2103 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2104 edecrypt (mp, t1_getbyte (mp));
2105 *mp->ps->t1_line_ptr++ = 0;
2107 mp->ps->t1_eexec_encrypt = true;
2108 if (!mp->ps->read_encoding_only)
2109 if (is_included (fm_cur))
2110 t1_putline (mp); /* to put the first four bytes */
2112 static void t1_stop_eexec (MP mp) {
2114 end_last_eexec_line ();
2115 if (!mp->ps->t1_pfa)
2116 t1_check_block_len (mp,true);
2118 c = edecrypt (mp, t1_getbyte (mp));
2119 if (!(c == 10 || c == 13)) {
2120 if (mp->ps->last_hexbyte == 0)
2123 mp_warn (mp,"unexpected data after eexec");
2126 mp->ps->t1_cs = false;
2127 mp->ps->t1_in_eexec = 2;
2129 static void t1_modify_fm (MP mp) {
2130 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2133 static void t1_modify_italic (MP mp) {
2134 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2139 const char *pdfname;
2149 static key_entry font_keys[FONT_KEYS_NUM] = {
2150 {"Ascent", "Ascender", 0, false},
2151 {"CapHeight", "CapHeight", 0, false},
2152 {"Descent", "Descender", 0, false},
2153 {"FontName", "FontName", 0, false},
2154 {"ItalicAngle", "ItalicAngle", 0, false},
2155 {"StemV", "StdVW", 0, false},
2156 {"XHeight", "XHeight", 0, false},
2157 {"FontBBox", "FontBBox", 0, false},
2169 @d ITALIC_ANGLE_CODE 4
2175 @d FONTBBOX4_CODE 10
2176 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2179 static void t1_scan_keys (MP mp, int tex_font,fm_entry *fm_cur) {
2183 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2184 if (t1_prefix ("/FontMatrix")) {
2188 if (t1_prefix ("/ItalicAngle")) {
2189 t1_modify_italic (mp);
2193 if (t1_prefix ("/FontType")) {
2194 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2195 if ((i = t1_scan_num (mp,p, 0)) != 1) {
2197 mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2198 mp_fatal_error(mp,s);
2202 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2203 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2205 if (key - font_keys == MAX_KEY_CODE)
2208 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2210 if ((k = key - font_keys) == FONTNAME_CODE) {
2213 remove_eol (p, mp->ps->t1_line_array);
2214 mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2215 mp_fatal_error(mp,s);
2217 r = ++p; /* skip the slash */
2218 if (is_included (fm_cur)) {
2219 /* save the fontname */
2220 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2221 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2222 mp->ps->fontname_buf[i]=0;
2224 if(is_subsetted (fm_cur)) {
2225 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2226 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2228 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2230 alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
2232 strncpy (r, fm_cur->subset_tag , 6);
2234 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2235 mp->ps->t1_line_ptr = eol (r);
2237 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2239 mp->ps->t1_line_ptr = eol (r);
2244 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2245 && (*p == '[' || *p == '{'))
2247 if (k == FONTBBOX1_CODE) {
2248 for (i = 0; i < 4; i++) {
2249 key[i].value = t1_scan_num (mp, p, &r);
2254 key->value = t1_scan_num (mp, p, 0);
2256 static void t1_scan_param (MP mp, int tex_font,fm_entry *fm_cur)
2258 static const char *lenIV = "/lenIV";
2259 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2261 if (t1_prefix (lenIV)) {
2262 mp->ps->t1_lenIV = t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2265 t1_scan_keys (mp, tex_font,fm_cur);
2267 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2268 if (glyph_names[b] != notdef) {
2269 mp_xfree (glyph_names[b]);
2270 glyph_names[b] = (char *) notdef;
2272 if (glyph_names[a] != notdef) {
2273 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2276 static void t1_builtin_enc (MP mp) {
2277 int i, a, b, c, counter = 0;
2280 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2282 if (t1_suffix ("def")) { /* predefined encoding */
2283 sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2284 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2285 for (i = 0; i < 256; i++)
2286 if (standard_glyph_names[i] == notdef)
2287 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2289 mp->ps->t1_builtin_glyph_names[i] =
2290 mp_xstrdup (mp,standard_glyph_names[i]);
2291 mp->ps->t1_encoding = ENC_STANDARD;
2294 mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2295 mp->ps->t1_buf_array);
2296 mp_fatal_error(mp,s);
2300 mp->ps->t1_encoding = ENC_BUILTIN;
2302 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2303 * not a predefined encoding
2305 * We have two possible forms of Encoding vector. The first case is
2307 * /Encoding [/a /b /c...] readonly def
2309 * and the second case can look like
2311 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2317 for (i = 0; i < 256; i++)
2318 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2319 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2320 r = strchr (mp->ps->t1_line_array, '[') + 1;
2324 for (p = mp->ps->t1_buf_array, r++;
2325 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2329 if (counter > 255) {
2331 (mp, "encoding vector contains more than 256 names");
2333 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2334 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2337 if (*r != 10 && *r != '%') {
2338 if (str_prefix (r, "] def")
2339 || str_prefix (r, "] readonly def"))
2343 remove_eol (r, mp->ps->t1_line_array);
2344 mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2345 mp->ps->t1_line_array);
2346 mp_fatal_error(mp,s);
2350 r = mp->ps->t1_line_array;
2352 } else { /* the second case */
2353 p = strchr (mp->ps->t1_line_array, 10);
2357 p = mp->ps->t1_line_array;
2360 check for `dup <index> <glyph> put'
2362 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2363 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2364 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2365 mp->ps->t1_builtin_glyph_names[i] =
2366 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2367 p = strstr (p, " put") + strlen (" put");
2371 check for `dup dup <to> exch <from> get put'
2373 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2374 && valid_code (a) && valid_code (b)) {
2375 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2376 p = strstr (p, " get put") + strlen (" get put");
2380 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2383 (p, "dup dup %i %i getinterval %i exch putinterval",
2384 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2385 && valid_code (c)) {
2386 for (i = 0; i < c; i++)
2387 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2388 p = strstr (p, " putinterval") + strlen (" putinterval");
2392 check for `def' or `readonly def'
2394 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2395 && strcmp (p, "def\n") == 0)
2398 skip an unrecognizable word
2401 while (*p != ' ' && *p != 10)
2409 static void t1_check_end (MP mp) {
2413 if (t1_prefix ("{restore}"))
2419 char *ff_name; /* base name of font file */
2420 char *ff_path; /* full path to font file */
2424 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2426 ff = check_ff_exist (mp, fm_cur);
2427 if (ff->ff_path != NULL) {
2428 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font);
2430 mp_warn (mp, "cannot open Type 1 font file for reading");
2433 t1_init_params (mp,open_name_prefix,fm_cur->ff_name);
2434 mp->ps->fontfile_found = true;
2438 static void t1_scan_only (MP mp, int tex_font, fm_entry *fm_cur) {
2441 t1_scan_param (mp,tex_font, fm_cur);
2443 while (mp->ps->t1_in_eexec == 0);
2444 t1_start_eexec (mp,fm_cur);
2447 t1_scan_param (mp,tex_font, fm_cur);
2449 while (!(t1_charstrings () || t1_subrs ()));
2452 static void t1_include (MP mp, int tex_font, fm_entry *fm_cur) {
2455 t1_scan_param (mp,tex_font, fm_cur);
2458 while (mp->ps->t1_in_eexec == 0);
2459 t1_start_eexec (mp,fm_cur);
2462 t1_scan_param (mp,tex_font, fm_cur);
2465 while (!(t1_charstrings () || t1_subrs ()));
2466 mp->ps->t1_cs = true;
2471 while (!t1_end_eexec ());
2473 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2478 while (!t1_cleartomark ());
2479 t1_check_end (mp); /* write "{restore}if" if found */
2484 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2486 mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2487 mp_fatal_error(mp,s);
2491 static const char **check_cs_token_pair (MP mp) {
2492 const char **p = (const char **) cs_token_pairs_list;
2493 for (; p[0] != NULL; ++p)
2494 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2499 static void cs_store (MP mp, boolean is_subr) {
2503 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2504 *mp->ps->t1_buf_ptr++ = *p++);
2505 *mp->ps->t1_buf_ptr = 0;
2507 subr = t1_scan_num (mp, p + 1, 0);
2509 ptr = mp->ps->subr_tab + subr;
2511 ptr = mp->ps->cs_ptr++;
2512 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2514 mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2515 mp_fatal_error(mp,s);
2517 if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
2518 ptr->glyph_name = (char *) notdef;
2520 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2522 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2523 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2524 (unsigned) (mp->ps->t1_cslen + 4));
2525 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2526 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2527 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2528 *mp->ps->t1_buf_ptr++ = 10;
2529 if (is_subr && mp->ps->cs_token_pair == NULL)
2530 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2531 ptr->len = mp->ps->t1_buf_ptr - mp->ps->t1_buf_array;
2532 ptr->cslen = mp->ps->t1_cslen;
2533 ptr->data = mp_xmalloc (mp,ptr->len , sizeof (byte));
2534 memcpy (ptr->data, mp->ps->t1_buf_array, ptr->len);
2538 #define store_subr(mp) cs_store(mp,true)
2539 #define store_cs(mp) cs_store(mp,false)
2541 #define CC_STACK_SIZE 24
2543 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2544 static cc_entry cc_tab[CS_MAX];
2545 static boolean is_cc_init = false;
2549 if (stack_ptr - cc_stack < (N)) \
2553 #define stack_error(N) { \
2555 mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2556 (int) N, (int)(stack_ptr - cc_stack)); \
2562 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2564 #define cc_push(V) *stack_ptr++ = V
2565 #define cc_clear() stack_ptr = cc_stack
2567 #define set_cc(N, B, A, C) \
2568 cc_tab[N].nargs = A; \
2569 cc_tab[N].bottom = B; \
2570 cc_tab[N].clear = C; \
2571 cc_tab[N].valid = true
2573 static void cc_init (void) {
2577 for (i = 0; i < CS_MAX; i++)
2578 cc_tab[i].valid = false;
2579 set_cc (CS_HSTEM, true, 2, true);
2580 set_cc (CS_VSTEM, true, 2, true);
2581 set_cc (CS_VMOVETO, true, 1, true);
2582 set_cc (CS_RLINETO, true, 2, true);
2583 set_cc (CS_HLINETO, true, 1, true);
2584 set_cc (CS_VLINETO, true, 1, true);
2585 set_cc (CS_RRCURVETO, true, 6, true);
2586 set_cc (CS_CLOSEPATH, false, 0, true);
2587 set_cc (CS_CALLSUBR, false, 1, false);
2588 set_cc (CS_RETURN, false, 0, false);
2590 |set_cc(CS_ESCAPE, false, 0, false);|
2592 set_cc (CS_HSBW, true, 2, true);
2593 set_cc (CS_ENDCHAR, false, 0, true);
2594 set_cc (CS_RMOVETO, true, 2, true);
2595 set_cc (CS_HMOVETO, true, 1, true);
2596 set_cc (CS_VHCURVETO, true, 4, true);
2597 set_cc (CS_HVCURVETO, true, 4, true);
2598 set_cc (CS_DOTSECTION, false, 0, true);
2599 set_cc (CS_VSTEM3, true, 6, true);
2600 set_cc (CS_HSTEM3, true, 6, true);
2601 set_cc (CS_SEAC, true, 5, true);
2602 set_cc (CS_SBW, true, 4, true);
2603 set_cc (CS_DIV, false, 2, false);
2604 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2605 set_cc (CS_POP, false, 0, false);
2606 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2612 @d cs_getchar(mp) cdecrypt(mp,*data++, &cr)
2614 @d mark_subr(mp,n) cs_mark(mp,0, n)
2615 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2616 @d SMALL_BUF_SIZE 256
2619 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2620 char buf[SMALL_BUF_SIZE];
2623 va_start (args, fmt);
2624 vsprintf (buf, fmt, args);
2626 if (cs_name == NULL) {
2627 mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2629 mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2634 static void cs_mark (MP mp, const char *cs_name, int subr)
2640 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2644 if (cs_name == NULL) {
2646 ptr = mp->ps->subr_tab + subr;
2650 if (mp->ps->cs_notdef != NULL &&
2651 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2652 ptr = mp->ps->cs_notdef;
2654 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2655 if (strcmp (ptr->glyph_name, cs_name) == 0)
2657 if (ptr == mp->ps->cs_ptr) {
2659 mp_snprintf (s,128,"glyph `%s' undefined", cs_name);
2663 if (ptr->glyph_name == notdef)
2664 mp->ps->cs_notdef = ptr;
2667 /* only marked CharString entries and invalid entries can be skipped;
2668 valid marked subrs must be parsed to keep the stack in sync */
2669 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2671 ptr->is_used = true;
2673 cs_len = ptr->cslen;
2674 data = ptr->data + 4;
2675 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2677 while (cs_len > 0) {
2679 b = cs_getchar (mp);
2683 else if (b <= 250) {
2685 a = ((b - 247) << 8) + 108 + cs_getchar (mp);
2686 } else if (b <= 254) {
2688 a = -((b - 251) << 8) - 108 - cs_getchar (mp);
2691 a = (cs_getchar (mp) & 0xff) << 24;
2692 a |= (cs_getchar (mp) & 0xff) << 16;
2693 a |= (cs_getchar (mp) & 0xff) << 8;
2694 a |= (cs_getchar (mp) & 0xff) << 0;
2695 if (sizeof (integer) > 4 && (a & 0x80000000))
2700 if (b == CS_ESCAPE) {
2701 b = cs_getchar (mp) + CS_1BYTE_MAX;
2705 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2711 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2715 if (stack_ptr - cc_stack < cc->nargs)
2716 cs_warn (mp,cs_name, subr,
2717 "less arguments on stack (%i) than required (%i)",
2718 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2719 else if (stack_ptr - cc_stack > cc->nargs)
2720 cs_warn (mp,cs_name, subr,
2721 "more arguments on stack (%i) than required (%i)",
2722 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2724 switch (cc - cc_tab) {
2729 if (!mp->ps->subr_tab[a1].valid) {
2730 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2738 case CS_CALLOTHERSUBR:
2739 if (cc_get (-1) == 3)
2740 lastargOtherSubr3 = cc_get (-3);
2741 a1 = cc_get (-2) + 2;
2745 cc_push (lastargOtherSubr3);
2746 /* the only case when we care about the value being pushed onto
2747 stack is when POP follows CALLOTHERSUBR (changing hints by
2755 mark_cs (mp,standard_glyph_names[a1]);
2756 mark_cs (mp,standard_glyph_names[a2]);
2765 cs_error: /* an error occured during parsing */
2768 ptr->is_used = false;
2771 static void t1_subset_ascii_part (MP mp, int tex_font, fm_entry *fm_cur)
2775 while (!t1_prefix ("/Encoding")) {
2776 t1_scan_param (mp,tex_font, fm_cur);
2780 t1_builtin_enc (mp);
2781 if (is_reencoded (fm_cur))
2782 mp->ps->t1_glyph_names = external_enc ();
2784 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2786 |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2787 make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2788 update_subset_tag ();
2791 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2792 t1_puts (mp,"/Encoding StandardEncoding def\n");
2795 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2796 for (i = 0, j = 0; i < 256; i++) {
2797 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2799 t1_printf (mp,"dup %i /%s put\n", (int) t1_char (i),
2800 mp->ps->t1_glyph_names[i]);
2803 /* We didn't mark anything for the Encoding array. */
2804 /* We add "dup 0 /.notdef put" for compatibility */
2805 /* with Acrobat 5.0. */
2807 t1_puts (mp,"dup 0 /.notdef put\n");
2808 t1_puts (mp,"readonly def\n");
2812 t1_scan_param (mp,tex_font, fm_cur);
2813 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
2816 while (mp->ps->t1_in_eexec == 0);
2819 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
2820 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
2822 static void cs_init (MP mp) {
2823 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2824 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2825 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2826 mp->ps->cs_token_pair = NULL;
2827 mp->ps->subr_tab = NULL;
2828 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2829 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2832 static void init_cs_entry ( cs_entry * cs) {
2834 cs->glyph_name = NULL;
2837 cs->is_used = false;
2841 static void t1_mark_glyphs (MP mp, int tex_font);
2843 static void t1_read_subrs (MP mp, int tex_font, fm_entry *fm_cur)
2848 while (!(t1_charstrings () || t1_subrs ())) {
2849 t1_scan_param (mp,tex_font, fm_cur);
2854 mp->ps->t1_cs = true;
2855 mp->ps->t1_scan = false;
2858 mp->ps->subr_size_pos = strlen ("/Subrs") + 1;
2859 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2860 mp->ps->subr_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2861 if (mp->ps->subr_size == 0) {
2862 while (!t1_charstrings ())
2866 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
2867 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,mp->ps->subr_size, sizeof (cs_entry));
2868 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2869 init_cs_entry (ptr);
2870 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2872 while (mp->ps->t1_cslen) {
2876 /* mark the first four entries without parsing */
2877 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2878 mp->ps->subr_tab[i].is_used = true;
2879 /* the end of the Subrs array might have more than one line so we need to
2880 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2881 the Subrs array followed by the CharStrings dict immediately (synthetic
2882 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2883 we will treat the font as synthetic and ignore everything until next
2886 #define POST_SUBRS_SCAN 5
2888 *mp->ps->t1_buf_array = 0;
2889 for (i = 0; i < POST_SUBRS_SCAN; i++) {
2890 if (t1_charstrings ())
2892 s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2893 alloc_array (t1_buf, s, T1_BUF_SIZE);
2894 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2897 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2898 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2899 suppose synthetic font */
2900 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2902 mp_xfree (ptr->data);
2903 mp_xfree (mp->ps->subr_tab);
2904 mp_xfree (mp->ps->subr_array_start);
2905 mp_xfree (mp->ps->subr_array_end);
2907 mp->ps->t1_cs = false;
2908 mp->ps->t1_synthetic = true;
2909 while (!(t1_charstrings () || t1_subrs ()))
2916 static void t1_flush_cs (MP mp, boolean is_subr)
2919 byte *r, *return_cs = NULL;
2920 cs_entry *tab, *end_tab, *ptr;
2921 char *start_line, *line_end;
2922 int count, size_pos;
2923 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2925 start_line = mp->ps->subr_array_start;
2926 line_end = mp->ps->subr_array_end;
2927 size_pos = mp->ps->subr_size_pos;
2928 tab = mp->ps->subr_tab;
2929 count = mp->ps->subr_max + 1;
2930 end_tab = mp->ps->subr_tab + count;
2932 start_line = mp->ps->cs_dict_start;
2933 line_end = mp->ps->cs_dict_end;
2934 size_pos = mp->ps->cs_size_pos;
2935 tab = mp->ps->cs_tab;
2936 end_tab = mp->ps->cs_ptr;
2937 count = mp->ps->cs_count;
2939 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2940 for (p = start_line; p - start_line < size_pos;)
2941 *mp->ps->t1_line_ptr++ = *p++;
2942 while (isdigit (*p))
2944 sprintf (mp->ps->t1_line_ptr, "%u", count);
2945 strcat (mp->ps->t1_line_ptr, p);
2946 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2949 /* create |return_cs| to replace unsused subr's */
2953 return_cs = mp_xmalloc (mp, (mp->ps->t1_lenIV + 1) , sizeof(byte));
2954 if ( mp->ps->t1_lenIV > 0) {
2955 for (cs_len = 0, r = return_cs; cs_len < mp->ps->t1_lenIV; cs_len++, r++)
2956 *r = cencrypt (mp,0x00, &cr);
2957 *r = cencrypt (mp,CS_RETURN, &cr);
2959 *return_cs = CS_RETURN;
2964 for (ptr = tab; ptr < end_tab; ptr++) {
2967 sprintf (mp->ps->t1_line_array, "dup %i %u", (int) (ptr - tab),
2970 sprintf (mp->ps->t1_line_array, "/%s %u", ptr->glyph_name, ptr->cslen);
2971 p = strend (mp->ps->t1_line_array);
2972 memcpy (p, ptr->data, ptr->len);
2973 mp->ps->t1_line_ptr = p + ptr->len;
2976 /* replace unsused subr's by |return_cs| */
2978 sprintf (mp->ps->t1_line_array, "dup %i %u%s ", (int) (ptr - tab),
2979 cs_len, mp->ps->cs_token_pair[0]);
2980 p = strend (mp->ps->t1_line_array);
2981 memcpy (p, return_cs, cs_len);
2982 mp->ps->t1_line_ptr = p + cs_len;
2984 sprintf (mp->ps->t1_line_array, " %s", mp->ps->cs_token_pair[1]);
2985 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2989 mp_xfree (ptr->data);
2990 if (ptr->glyph_name != notdef)
2991 mp_xfree (ptr->glyph_name);
2993 sprintf (mp->ps->t1_line_array, "%s", line_end);
2994 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2997 mp_xfree (return_cs);
2999 mp_xfree (start_line);
3000 mp_xfree (line_end);
3003 static void t1_mark_glyphs (MP mp, int tex_font)
3006 char *charset = extra_charset ();
3009 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
3010 if (mp->ps->cs_tab != NULL)
3011 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3013 ptr->is_used = true;
3014 if (mp->ps->subr_tab != NULL) {
3015 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3017 ptr->is_used = true;
3018 mp->ps->subr_max = mp->ps->subr_size - 1;
3022 mark_cs (mp,notdef);
3023 for (i = 0; i < 256; i++)
3024 if (is_used_char (i)) {
3025 if (mp->ps->t1_glyph_names[i] == notdef) {
3027 mp_snprintf(S,128, "character %i is mapped to %s", i, notdef);
3030 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3032 if (charset == NULL)
3034 g = s = charset + 1; /* skip the first '/' */
3037 while (*s != '/' && s < r)
3039 *s = 0; /* terminate g by rewriting '/' to 0 */
3044 if (mp->ps->subr_tab != NULL)
3045 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3046 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3048 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3049 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3052 static void t1_subset_charstrings (MP mp, int tex_font)
3055 mp->ps->cs_size_pos =
3056 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3057 - mp->ps->t1_line_array + 1;
3058 /* |cs_size_pos| points to the number indicating
3059 dict size after "/CharStrings" */
3060 mp->ps->cs_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3061 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,mp->ps->cs_size, sizeof(cs_entry));
3062 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3063 init_cs_entry (ptr);
3064 mp->ps->cs_notdef = NULL;
3065 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3067 while (mp->ps->t1_cslen) {
3071 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3072 t1_mark_glyphs (mp,tex_font);
3073 if (mp->ps->subr_tab != NULL) {
3074 if (mp->ps->cs_token_pair == NULL)
3076 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3079 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3085 static void t1_subset_end (MP mp)
3087 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3088 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3092 while (!t1_end_eexec ())
3093 t1_getline (mp); /* ignore the rest */
3094 t1_putline (mp); /* write "mark currentfile closefile" */
3096 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3101 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3102 while (!t1_cleartomark ()) {
3106 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3107 t1_check_end (mp); /* write "{restore}if" if found */
3111 static int t1_updatefm (MP mp, int f, fm_entry *fm)
3114 mp->ps->read_encoding_only = true;
3115 if (!t1_open_fontfile (mp,fm,NULL)) {
3118 t1_scan_only (mp,f, fm);
3119 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3121 while (*p != ' ' && *p != 0)
3125 t1_close_font_file (mp,"");
3130 static void writet1 (MP mp, int tex_font, fm_entry *fm_cur) {
3131 int save_selector = mp->selector;
3132 mp_normalize_selector(mp);
3133 mp->ps->read_encoding_only = false;
3134 if (!is_included (fm_cur)) { /* scan parameters from font file */
3135 if (!t1_open_fontfile (mp,fm_cur,"{"))
3137 t1_scan_only (mp,tex_font, fm_cur);
3138 t1_close_font_file (mp,"}");
3141 if (!is_subsetted (fm_cur)) { /* include entire font */
3142 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3144 t1_include (mp,tex_font,fm_cur);
3145 t1_close_font_file (mp,">>");
3148 /* partial downloading */
3149 if (!t1_open_fontfile (mp,fm_cur,"<"))
3151 t1_subset_ascii_part (mp,tex_font,fm_cur);
3152 t1_start_eexec (mp,fm_cur);
3155 t1_read_subrs (mp,tex_font, fm_cur);
3156 t1_subset_charstrings (mp,tex_font);
3158 t1_close_font_file (mp,">");
3159 mp->selector = save_selector;
3163 static void t1_free (MP mp);
3166 static void t1_free (MP mp) {
3167 mp_xfree (mp->ps->t1_line_array);
3168 mp_xfree (mp->ps->t1_buf_array);
3172 @* \[44d] Embedding fonts.
3174 @ The |tfm_num| is officially of type |font_number|, but that
3175 type does not exist yet at this point in the output order.
3179 char *tfm_name; /* TFM file name */
3180 char *ps_name; /* PostScript name */
3181 integer flags; /* font flags */
3182 char *ff_name; /* font file name */
3183 char *subset_tag; /* pseudoUniqueTag for subsetted font */
3184 enc_entry *encoding; /* pointer to corresponding encoding */
3185 unsigned int tfm_num; /* number of the TFM refering this entry */
3186 unsigned short type; /* font type (T1/TTF/...) */
3187 short slant; /* SlantFont */
3188 short extend; /* ExtendFont */
3189 integer ff_objnum; /* FontFile object number */
3190 integer fn_objnum; /* FontName/BaseName object number */
3191 integer fd_objnum; /* FontDescriptor object number */
3192 char *charset; /* string containing used glyphs */
3193 boolean all_glyphs; /* embed all glyphs? */
3194 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
3195 short tfm_avail; /* flags whether a tfm is available */
3196 short pid; /* Pid for truetype fonts */
3197 short eid; /* Eid for truetype fonts */
3203 #define FONTNAME_BUF_SIZE 128
3204 boolean fontfile_found;
3205 boolean is_otf_font;
3206 char fontname_buf[FONTNAME_BUF_SIZE];
3214 @d set_included(fm) ((fm)->type |= F_INCLUDED)
3215 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
3216 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
3217 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
3219 @d is_included(fm) ((fm)->type & F_INCLUDED)
3220 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
3221 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
3222 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
3223 @d is_reencoded(fm) ((fm)->encoding != NULL)
3224 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
3225 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
3227 @d fm_slant(fm) (fm)->slant
3228 @d fm_extend(fm) (fm)->extend
3229 @d fm_fontfile(fm) (fm)->ff_name
3232 static boolean mp_font_is_reencoded (MP mp, int f);
3233 static boolean mp_font_is_included (MP mp, int f);
3234 static boolean mp_font_is_subsetted (MP mp, int f);
3237 static boolean mp_font_is_reencoded (MP mp, int f) {
3239 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3241 && (fm->ps_name != NULL)
3242 && is_reencoded (fm))
3247 static boolean mp_font_is_included (MP mp, int f) {
3249 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3251 && (fm->ps_name != NULL && fm->ff_name != NULL)
3252 && is_included (fm))
3257 static boolean mp_font_is_subsetted (MP mp, int f) {
3259 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
3261 && (fm->ps_name != NULL && fm->ff_name != NULL)
3262 && is_included (fm) && is_subsetted (fm))
3268 @ @<Exported function headers@>=
3269 char * mp_fm_encoding_name (MP mp, int f);
3270 char * mp_fm_font_name (MP mp, int f);
3273 static char * mp_fm_font_subset_name (MP mp, int f);
3276 @c char * mp_fm_encoding_name (MP mp, int f) {
3279 if (mp_has_fm_entry (mp, f, &fm)) {
3280 if (fm != NULL && (fm->ps_name != NULL)) {
3281 if (is_reencoded (fm)) {
3283 if (e->enc_name!=NULL)
3284 return mp_xstrdup(mp,e->enc_name);
3290 print_err ("fontmap encoding problems for font ");
3291 mp_print(mp,mp->font_name[f]);
3295 char * mp_fm_font_name (MP mp, int f) {
3297 if (mp_has_fm_entry (mp, f,&fm)) {
3298 if (fm != NULL && (fm->ps_name != NULL)) {
3299 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3300 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3301 if (t1_updatefm(mp,f,fm)) {
3302 mp->font_ps_name_fixed[f] = true;
3304 print_err ("font loading problems for font ");
3305 mp_print(mp,mp->font_name[f]);
3309 return mp_xstrdup(mp,fm->ps_name);
3312 print_err ("fontmap name problems for font ");
3313 mp_print(mp,mp->font_name[f]);
3318 static char * mp_fm_font_subset_name (MP mp, int f) {
3320 if (mp_has_fm_entry (mp, f, &fm)) {
3321 if (fm != NULL && (fm->ps_name != NULL)) {
3322 if (is_subsetted(fm)) {
3323 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3324 mp_snprintf(s,strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3327 return mp_xstrdup(mp,fm->ps_name);
3331 print_err ("fontmap name problems for font ");
3332 mp_print(mp,mp->font_name[f]);
3338 static integer mp_fm_font_slant (MP mp, int f);
3339 static integer mp_fm_font_extend (MP mp, int f);
3342 @c static integer mp_fm_font_slant (MP mp, int f) {
3344 if (mp_has_fm_entry (mp, f, &fm)) {
3345 if (fm != NULL && (fm->ps_name != NULL)) {
3351 static integer mp_fm_font_extend (MP mp, int f) {
3353 if (mp_has_fm_entry (mp, f, &fm)) {
3354 if (fm != NULL && (fm->ps_name != NULL)) {
3362 static boolean mp_do_ps_font (MP mp, font_number f);
3364 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3366 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3369 if (is_truetype(fm_cur) ||
3370 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3373 if (is_included(fm_cur)) {
3374 mp_ps_print_nl(mp,"%%BeginResource: font ");
3375 if (is_subsetted(fm_cur)) {
3376 mp_ps_print(mp, fm_cur->subset_tag);
3377 mp_ps_print_char(mp,'-');
3379 mp_ps_print(mp, fm_cur->ps_name);
3381 writet1 (mp,f,fm_cur);
3382 mp_ps_print_nl(mp,"%%EndResource");
3388 @ Included subset fonts do not need and encoding vector, make
3389 sure we skip that case.
3392 static void mp_list_used_resources (MP mp, int prologues, int procset);
3394 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
3395 font_number f; /* fonts used in a text node or as loop counters */
3396 int ff; /* a loop counter */
3397 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3400 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
3402 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3405 for (f=null_font+1;f<=mp->last_fnum;f++) {
3406 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3407 for (ff=ldf;ff>=null_font;ff--) {
3408 if ( mp_has_font_size(mp,ff) )
3409 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3412 if ( mp_font_is_subsetted(mp,f) )
3414 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
3415 (unsigned)mp->max_print_line )
3416 mp_ps_print_nl(mp, "%%+ encoding");
3419 mp_ps_print_nl(mp, "%%+ encoding");
3421 mp_ps_print_char(mp, ' ');
3422 mp_ps_print(mp, mp->font_enc_name[f]);
3430 for (f=null_font+1;f<=mp->last_fnum;f++) {
3431 if ( mp_has_font_size(mp,f) ) {
3432 for (ff=ldf;ff>=null_font;ff--) {
3433 if ( mp_has_font_size(mp,ff) )
3434 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3437 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
3438 (unsigned)mp->max_print_line )
3439 mp_ps_print_nl(mp, "%%+ font");
3442 mp_ps_print_nl(mp, "%%+ font");
3444 mp_ps_print_char(mp, ' ');
3445 if ( (prologues==3)&&
3446 (mp_font_is_subsetted(mp,f)) )
3447 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3449 mp_ps_print(mp, mp->font_ps_name[f]);
3459 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
3461 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
3462 font_number f; /* fonts used in a text node or as loop counters */
3463 int ff; /* a loop counter */
3464 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3467 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3469 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3472 for (f=null_font+1;f<=mp->last_fnum;f++) {
3473 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3474 for (ff=ldf;ff>= null_font;ff++) {
3475 if ( mp_has_font_size(mp,ff) )
3476 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3479 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3481 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(unsigned)mp->max_print_line )
3482 mp_ps_print_nl(mp, "%%+ encoding");
3485 mp_ps_print_nl(mp, "%%+ encoding");
3487 mp_ps_print_char(mp, ' ');
3488 mp_ps_print(mp, mp->font_enc_name[f]);
3497 for (f=null_font+1;f<=mp->last_fnum;f++) {
3498 if ( mp_has_font_size(mp,f) ) {
3499 for (ff=ldf;ff>= null_font;ff--) {
3500 if ( mp_has_font_size(mp,ff) )
3501 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3504 if ( ! mp_font_is_included(mp,f) )
3506 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3507 mp_ps_print_nl(mp, "%%+ font");
3510 mp_ps_print_nl(mp, "%%+ font");
3512 mp_ps_print_char(mp, ' ');
3513 if ( mp_font_is_subsetted(mp,f) )
3514 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3516 mp_ps_print(mp, mp->font_ps_name[f]);
3526 @ @<Declarations...@>=
3527 static void mp_list_needed_resources (MP mp, int prologues);
3529 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
3530 font_number f; /* fonts used in a text node or as loop counters */
3531 int ff; /* a loop counter */
3532 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3536 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3537 if ( mp_has_font_size(mp,f)) {
3538 for (ff=ldf;ff>=null_font;ff--) {
3539 if ( mp_has_font_size(mp,ff) )
3540 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3543 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3545 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3546 mp_ps_print_nl(mp, "%%+ font");
3549 mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
3551 mp_ps_print_char(mp, ' ');
3552 mp_ps_print(mp, mp->font_ps_name[f]);
3558 if ( ! firstitem ) {
3562 for (f=null_font+1;f<= mp->last_fnum;f++) {
3563 if ( mp_has_font_size(mp,f) ) {
3564 for (ff=ldf;ff>=null_font;ff-- ) {
3565 if ( mp_has_font_size(mp,ff) )
3566 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3569 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3571 mp_ps_print(mp, "%%IncludeResource: font ");
3572 mp_ps_print(mp, mp->font_ps_name[f]);
3583 static void mp_write_font_definition (MP mp, font_number f, int prologues);
3587 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3588 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3590 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
3591 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3592 (mp_fm_font_extend(mp,f)!=0)||
3593 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3594 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3595 if ( (mp_font_is_subsetted(mp,f))&&
3596 (mp_font_is_included(mp,f))&&(prologues==3))
3597 mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3599 mp_ps_name_out(mp, mp->font_ps_name[f],true);
3600 mp_ps_print(mp, " fcp");
3602 if ( applied_reencoding(f) ) {
3603 mp_ps_print(mp, "/Encoding ");
3604 mp_ps_print(mp, mp->font_enc_name[f]);
3605 mp_ps_print(mp, " def ");
3607 if ( mp_fm_font_slant(mp,f)!=0 ) {
3608 mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
3609 mp_ps_print(mp, " SlantFont ");
3611 if ( mp_fm_font_extend(mp,f)!=0 ) {
3612 mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3613 mp_ps_print(mp, " ExtendFont ");
3615 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3616 mp_ps_print(mp, " 890 ScaleFont ");
3617 mp_ps_print(mp, " 277 SlantFont ");
3619 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3620 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3621 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3622 mp_ps_print(mp, "/space[0 -278]def ");
3623 mp_ps_print(mp, "/a12[-904 -939]def ");
3624 mp_ps_print(mp, "end def ");
3626 mp_ps_print(mp, "currentdict end");
3628 mp_ps_print_defined_name(mp,f,prologues);
3629 mp_ps_print(mp, " exch definefont pop");
3635 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
3638 @c static void mp_ps_print_defined_name(MP mp, font_number A, int prologues) {
3639 mp_ps_print(mp, " /");
3640 if ((mp_font_is_subsetted(mp,(A)))&&
3641 (mp_font_is_included(mp,(A)))&&(prologues==3))
3642 mp_ps_print(mp, mp_fm_font_subset_name(mp,(A)));
3644 mp_ps_print(mp, mp->font_ps_name[(A)]);
3645 if ( mp_xstrcmp(mp->font_name[(A)],"psyrgo")==0 )
3646 mp_ps_print(mp, "-Slanted");
3647 if ( mp_xstrcmp(mp->font_name[(A)],"zpzdr-reversed")==0 )
3648 mp_ps_print(mp, "-Reverse");
3649 if ( applied_reencoding((A)) ) {
3650 mp_ps_print(mp, "-");
3651 mp_ps_print(mp, mp->font_enc_name[(A)]);
3653 if ( mp_fm_font_slant(mp,(A))!=0 ) {
3654 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,(A))) ;
3656 if ( mp_fm_font_extend(mp,(A))!=0 ) {
3657 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,(A)));
3661 @ @<Include encodings and fonts for edge structure~|h|@>=
3662 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
3663 @<Embed fonts that are available@>
3665 @ @<Embed fonts that are available@>=
3668 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3671 for (f=null_font+1;f<=mp->last_fnum;f++) {
3672 if ( cur_fsize[f]!=null ) {
3673 if (prologues==3 ) {
3674 if ( ! mp_do_ps_font(mp,f) ) {
3675 if ( mp_has_fm_entry(mp,f, NULL) ) {
3676 print_err("Font embedding failed");
3681 cur_fsize[f]=link(cur_fsize[f]);
3682 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3686 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3688 } while (! done_fonts);
3691 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3694 mp_apply_mark_string_chars(mp, h, next_size);
3697 @ We also need to keep track of which characters are used in text nodes
3698 in the edge structure that is being shipped out. This is done by procedures
3699 that use the left-over |b3| field in the |char_info| words; i.e.,
3700 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3703 enum mp_char_mark_state {mp_unused=0, mp_used};
3706 void mp_mark_string_chars (MP mp,font_number f, char *s) ;
3709 void mp_mark_string_chars (MP mp,font_number f, char *s) {
3710 integer b; /* |char_base[f]| */
3711 ASCII_code bc,ec; /* only characters between these bounds are marked */
3712 char *k; /* an index into string |s| */
3718 if ( (*k>=bc)&&(*k<=ec) )
3719 mp->font_info[b+*k].qqqq.b3=mp_used;
3726 void mp_unmark_font (MP mp,font_number f) ;
3729 void mp_unmark_font (MP mp,font_number f) {
3730 int k; /* an index into |font_info| */
3731 for (k= mp->char_base[f]+mp->font_bc[f];
3732 k<=mp->char_base[f]+mp->font_ec[f];
3734 mp->font_info[k].qqqq.b3=mp_unused;
3739 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) ;
3742 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
3743 quarterword next_size; /* the size index for fonts being listed */
3744 pointer *cur_fsize; /* current positions in |font_sizes| */
3745 boolean done_fonts; /* have we finished listing the fonts in the header? */
3746 font_number f; /* a font number for loops */
3747 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3748 mp_list_used_resources(mp, prologues, procset);
3749 mp_list_supplied_resources(mp, prologues, procset);
3750 mp_list_needed_resources(mp, prologues);
3751 mp_ps_print_nl(mp, "%%EndComments");
3752 mp_ps_print_nl(mp, "%%BeginProlog");
3754 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
3756 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3757 mp_ps_print_nl(mp, "/bd{bind def}bind def"
3758 "/fshow {exch findfont exch scalefont setfont show}bd");
3759 if ( procset>0 ) @<Print the procset@>;
3760 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
3761 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3762 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3763 "/fmd{/FontMatrix exch def}bd");
3764 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3765 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3766 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3767 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3768 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3769 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3770 mp_ps_print_nl(mp, "%%EndResource");
3771 @<Include encodings and fonts for edge structure~|h|@>;
3772 mp_ps_print_nl(mp, "%%EndProlog");
3773 mp_ps_print_nl(mp, "%%BeginSetup");
3775 for (f=null_font+1;f<=mp->last_fnum;f++) {
3776 if ( mp_has_font_size(mp,f) ) {
3777 if ( mp_has_fm_entry(mp,f,NULL) ) {
3778 mp_write_font_definition(mp,f, prologues);
3779 mp_ps_name_out(mp, mp->font_name[f],true);
3780 mp_ps_print_defined_name(mp,f, prologues);
3781 mp_ps_print(mp, " def");
3784 mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3786 mp_ps_name_out(mp, mp->font_name[f],true);
3787 mp_ps_name_out(mp, mp->font_name[f],true);
3788 mp_ps_print(mp, " def");
3793 mp_ps_print_nl(mp, "%%EndSetup");
3794 mp_ps_print_nl(mp, "%%Page: 1 1");
3796 mp_xfree(cur_fsize);
3800 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues);
3805 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) {
3806 quarterword next_size; /* the size index for fonts being listed */
3807 pointer *cur_fsize; /* current positions in |font_sizes| */
3808 int ff; /* a loop counter */
3809 boolean done_fonts; /* have we finished listing the fonts in the header? */
3810 font_number f; /* a font number for loops */
3811 scaled ds; /* design size and scale factor for a text node */
3812 font_number ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3813 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3814 if ( prologues>0 ) {
3815 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3816 |font_sizes| and eliminate duplicates@>;
3819 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3820 do { done_fonts=true;
3821 for (f=null_font+1;f<=mp->last_fnum;f++) {
3822 if ( cur_fsize[f]!=null ) {
3823 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3825 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
3827 if ( ! done_fonts ) {
3828 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3831 } while (! done_fonts);
3833 mp_xfree(cur_fsize);
3837 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3838 for (f=null_font+1;f<= mp->last_fnum;f++)
3839 cur_fsize[f]=mp->font_sizes[f]
3841 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3842 so we carefully remove duplicates. There is no harm in using a slow, brute-force
3845 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3848 for (f=null_font+1;f<= mp->last_fnum;f++) {
3849 if ( mp->font_sizes[f]!=null ) {
3850 if ( ldf==null_font )
3851 mp_ps_print_nl(mp, "%%DocumentFonts:");
3852 for (ff=ldf;ff>=null_font;ff--) {
3853 if ( mp->font_sizes[ff]!=null )
3854 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3857 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3858 mp_ps_print_nl(mp, "%%+");
3859 mp_ps_print_char(mp, ' ');
3860 mp_ps_print(mp, mp->font_ps_name[f]);
3869 static void mp_hex_digit_out (MP mp,small_number d) {
3870 if ( d<10 ) mp_ps_print_char(mp, d+'0');
3871 else mp_ps_print_char(mp, d+'a'-10);
3874 @ We output the marks as a hexadecimal bit string starting at |c| or
3875 |font_bc[f]|, whichever is greater. If the output has to be truncated
3876 to avoid exceeding |emergency_line_length| the return value says where to
3877 start scanning next time.
3880 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3883 @d emergency_line_length 255
3884 /* \ps\ output lines can be this long in unusual circumstances */
3887 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3888 eight_bits bc,ec; /* only encode characters between these bounds */
3889 integer lim; /* the maximum number of marks to encode before truncating */
3890 int p; /* |font_info| index for the current character */
3891 int d,b; /* used to construct a hexadecimal digit */
3892 lim=4*(emergency_line_length-mp->ps->ps_offset-4);
3896 @<Restrict the range |bc..ec| so that it contains no unused characters
3897 at either end and has length at most |lim|@>;
3898 @<Print the initial label indicating that the bitmap starts at |bc|@>;
3899 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3900 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3906 @ We could save time by setting the return value before the loop that
3907 decrements |ec|, but there is no point in being so tricky.
3909 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3910 p=mp->char_base[f]+bc;
3911 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3914 if ( ec>=bc+lim ) ec=bc+lim-1;
3915 p=mp->char_base[f]+ec;
3916 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3920 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3921 mp_ps_print_char(mp, ' ');
3922 mp_hex_digit_out(mp, bc / 16);
3923 mp_hex_digit_out(mp, bc % 16);
3924 mp_ps_print_char(mp, ':')
3928 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3930 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3932 mp_hex_digit_out(mp, d);
3935 if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
3938 mp_hex_digit_out(mp, d)
3941 @ Here is a simple function that determines whether there are any marked
3942 characters in font~|f| with character code at least~|c|.
3945 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
3948 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
3949 int p; /* |font_info| index for the current character */
3950 for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
3951 if ( mp->font_info[p].qqqq.b3==mp_used )
3958 @ If the file name is so long that it can't be printed without exceeding
3959 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
3960 line. We might have to repeat line in order to get the character usage
3961 information to fit within |emergency_line_length|.
3963 TODO: these two defines are also defined in mp.w!
3965 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
3966 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
3968 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
3970 while ( mp_check_ps_marks(mp, f,t) ) {
3971 mp_ps_print_nl(mp, "%*Font: ");
3972 if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
3974 mp_ps_print(mp, mp->font_name[f]);
3975 mp_ps_print_char(mp, ' ');
3976 ds=(mp->font_dsize[f] + 8) / 16;
3977 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
3978 if ( mp->ps->ps_offset+12>emergency_line_length ) break;
3979 mp_ps_print_char(mp, ' ');
3980 mp_ps_print_scaled(mp, ds);
3981 if ( mp->ps->ps_offset+5>emergency_line_length ) break;
3982 t=mp_ps_marks_out(mp, f,t);
3984 cur_fsize[f]=link(cur_fsize[f]);
3987 @ @<Print the procset@>=
3989 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
3990 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
3991 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
3992 "/p{closepath}bd/n{newpath}bd");
3993 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
3994 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
3995 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
3996 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
3997 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4001 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4002 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
4003 might not have reasonable |font_ps_name| entries, but we just charge ahead
4004 anyway. The user should not make \&{prologues} positive if this will cause
4006 @:prologues_}{\&{prologues} primitive@>
4009 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset);
4012 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
4015 ldf = mp_print_font_comments (mp, h, prologues);
4017 if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
4018 mp_read_psname_table(mp);
4019 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4020 if ( (prologues>0)||(procset>0) ) {
4021 if ( ldf!=null_font ) {
4022 if ( prologues>0 ) {
4023 for (f=null_font+1;f<=mp->last_fnum;f++) {
4024 if ( mp_has_font_size(mp,f) ) {
4025 mp_ps_name_out(mp, mp->font_name[f],true);
4026 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4027 mp_ps_print(mp, " def");
4032 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4038 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4039 if ( (prologues>0)&&(ldf!=null_font) )
4041 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4043 mp_ps_print_nl(mp, "/bd{bind def}bind def");
4044 @<Print the procset@>;
4045 mp_ps_print_nl(mp, "%%EndResource");
4049 mp_ps_print(mp, "%%EndProlog");
4050 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4053 @ \MP\ used to have one single routine to print to both `write' files
4054 and the PostScript output. Web2c redefines ``Character |k| cannot be
4055 printed'', and that resulted in some bugs where 8-bit characters were
4056 written to the PostScript file (reported by Wlodek Bzyl).
4058 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4059 a plain space, since that makes it easier to parse the result file
4062 @<Character |k| is not allowed in PostScript output@>=
4065 @ We often need to print a pair of coordinates.
4068 void mp_ps_pair_out (MP mp,scaled x, scaled y) {
4070 mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
4071 mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
4075 void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4078 void mp_ps_print_cmd (MP mp, const char *l, const char *s) {
4079 if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4080 else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4084 void mp_ps_print_cmd (MP mp, const char *l, const char *s) ;
4087 void mp_ps_string_out (MP mp, const char *s) {
4088 ASCII_code k; /* bits to be converted to octal */
4089 mp_ps_print(mp, "(");
4091 if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4092 mp_ps_print_char(mp, '\\');
4095 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4096 mp_ps_print_char(mp, '\\');
4097 mp_ps_print_char(mp, '0'+(k / 64));
4098 mp_ps_print_char(mp, '0'+((k / 8) % 8));
4099 mp_ps_print_char(mp, '0'+(k % 8));
4101 if ( (k=='(')||(k==')')||(k=='\\') )
4102 mp_ps_print_char(mp, '\\');
4103 mp_ps_print_char(mp, k);
4106 mp_ps_print_char(mp, ')');
4110 void mp_ps_string_out (MP mp, const char *s) ;
4112 @ This is a define because the function does not use its |mp| argument.
4114 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4117 static boolean mp_do_is_ps_name (char *s) {
4118 ASCII_code k; /* the character being checked */
4120 if ( (k<=' ')||(k>'~') ) return false;
4121 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4122 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4128 void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4131 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4132 ps_room(strlen(s)+2);
4133 mp_ps_print_char(mp, ' ');
4134 if ( mp_is_ps_name(mp, s) ) {
4135 if ( lit ) mp_ps_print_char(mp, '/');
4138 mp_ps_string_out(mp, s);
4139 if ( ! lit ) mp_ps_print(mp, "cvx ");
4140 mp_ps_print(mp, "cvn");
4145 @ These special comments described in the {\sl PostScript Language Reference
4146 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4147 We can't normally output ``conforming'' \ps\ because
4148 the structuring conventions don't allow us to say ``Please make sure the
4149 following characters are downloaded and define the \.{fshow} macro to access
4152 The exact bounding box is written out if |mp_prologues<0|, although this
4153 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4154 accurately. (Overfull boxes are avoided if an illustration is made to
4155 match a given \.{\char`\\hsize}.)
4158 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues);
4161 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) {
4163 mp_ps_print(mp, "%!PS");
4165 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
4166 mp_ps_print_nl(mp, "%%BoundingBox: ");
4167 if ( hh->_minx>hh->_maxx) {
4168 mp_ps_print(mp, "0 0 0 0");
4169 } else if ( prologues<0 ) {
4170 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4171 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4173 mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->_minx),mp_floor_scaled(mp, hh->_miny));
4174 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->_maxx),-mp_floor_scaled(mp, -hh->_maxy));
4176 mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
4177 if ( hh->_minx>hh->_maxx ) {
4178 mp_ps_print(mp, "0 0 0 0");
4180 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4181 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4183 mp_ps_print_nl(mp, "%%Creator: MetaPost ");
4184 mp_ps_print(mp, mp_metapost_version(mp));
4185 mp_ps_print_nl(mp, "%%CreationDate: ");
4186 mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
4187 mp_ps_print_char(mp, '.');
4188 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month]));
4189 mp_ps_print_char(mp, '.');
4190 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
4191 mp_ps_print_char(mp, ':');
4192 t=mp_round_unscaled(mp, mp->internal[mp_time]);
4193 mp_ps_print_dd(mp, t / 60);
4194 mp_ps_print_dd(mp, t % 60);
4195 mp_ps_print_nl(mp, "%%Pages: 1");
4198 @ The most important output procedure is the one that gives the \ps\ version of
4202 #define gr_left_type(A) (A)->left_type_field
4203 #define gr_right_type(A) (A)->right_type_field
4204 #define gr_x_coord(A) (A)->x_coord_field
4205 #define gr_y_coord(A) (A)->y_coord_field
4206 #define gr_left_x(A) (A)->left_x_field
4207 #define gr_left_y(A) (A)->left_y_field
4208 #define gr_right_x(A) (A)->right_x_field
4209 #define gr_right_y(A) (A)->right_y_field
4210 #define gr_next_knot(A) (A)->next_field
4211 #define gr_originator(A) (A)->originator_field
4212 typedef struct mp_knot {
4213 unsigned short left_type_field;
4214 unsigned short right_type_field;
4215 scaled x_coord_field;
4216 scaled y_coord_field;
4217 scaled left_x_field;
4218 scaled left_y_field;
4219 scaled right_x_field;
4220 scaled right_y_field;
4221 struct mp_knot * next_field;
4222 quarterword originator_field;
4226 mp_knot * mp_gr_insert_knot (MP mp, mp_knot *q, scaled x, scaled y) {
4227 /* returns the inserted knot */
4228 mp_knot *r; /* the new knot */
4229 r= mp_xmalloc(mp, 1, sizeof (mp_knot));
4230 gr_next_knot(r)=gr_next_knot(q); gr_next_knot(q)=r;
4231 gr_right_x(r)=gr_right_x(q);
4232 gr_right_y(r)=gr_right_y(q);
4235 gr_right_x(q)=gr_x_coord(q);
4236 gr_right_y(q)=gr_y_coord(q);
4237 gr_left_x(r)=gr_x_coord(r);
4238 gr_left_y(r)=gr_y_coord(r);
4239 gr_left_type(r)=mp_explicit;
4240 gr_right_type(r)=mp_explicit;
4241 gr_originator(r)=mp_program_code;
4246 @ If we want to duplicate a knot node, we can say |copy_knot|:
4249 mp_knot *mp_gr_copy_knot (MP mp, mp_knot *p) {
4250 mp_knot *q; /* the copy */
4251 q = mp_xmalloc(mp, 1, sizeof (mp_knot));
4252 memcpy(q,p,sizeof (mp_knot));
4253 gr_next_knot(q)=NULL;
4257 @ The |copy_path| routine makes a clone of a given path.
4260 mp_knot *mp_gr_copy_path (MP mp, mp_knot *p) {
4261 mp_knot *q, *pp, *qq; /* for list manipulation */
4264 q=mp_gr_copy_knot(mp, p);
4268 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4269 qq=gr_next_knot(qq);
4270 pp=gr_next_knot(pp);
4276 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4277 returns a pointer to the first node of the copy, if the path is a cycle,
4278 but to the final node of a non-cyclic copy. The global
4279 variable |path_tail| will point to the final node of the original path;
4280 this trick makes it easier to implement `\&{doublepath}'.
4282 All node types are assumed to be |endpoint| or |explicit| only.
4284 This function is currenly unused.
4287 mp_knot * mp_gr_htap_ypoc (MP mp, mp_knot *p) {
4288 mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4289 q=mp_xmalloc(mp, 1, sizeof (mp_knot)); /* this will correspond to |p| */
4292 gr_right_type(qq)=gr_left_type(pp);
4293 gr_left_type(qq)=gr_right_type(pp);
4294 gr_x_coord(qq)=gr_x_coord(pp);
4295 gr_y_coord(qq)=gr_y_coord(pp);
4296 gr_right_x(qq)=gr_left_x(pp);
4297 gr_right_y(qq)=gr_left_y(pp);
4298 gr_left_x(qq)=gr_right_x(pp);
4299 gr_left_y(qq)=gr_right_y(pp);
4300 gr_originator(qq)=gr_originator(pp);
4301 if ( gr_next_knot(pp)==p ) {
4303 /* |mp->path_tail=pp;| */ /* ? */
4306 rr=mp_xmalloc(mp, 1, sizeof (mp_knot));
4307 gr_next_knot(rr)=qq;
4309 pp=gr_next_knot(pp);
4313 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4314 calling the following subroutine.
4317 void mp_do_gr_toss_knot_list (mp_knot *p) ;
4320 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4323 void mp_do_gr_toss_knot_list (mp_knot * p) {
4324 mp_knot *q; /* the node being freed */
4325 mp_knot *r; /* the next node */
4338 void mp_gr_ps_path_out (MP mp, mp_knot *h) {
4339 mp_knot *p, *q; /* for scanning the path */
4340 scaled d; /* a temporary value */
4341 boolean curved; /* |true| unless the cubic is almost straight */
4343 mp_ps_print_cmd(mp, "newpath ","n ");
4344 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4345 mp_ps_print_cmd(mp, "moveto","m");
4348 if ( gr_right_type(p)==mp_endpoint ) {
4349 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4353 @<Start a new line and print the \ps\ commands for the curve from
4357 mp_ps_print_cmd(mp, " closepath"," p");
4360 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4362 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4365 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4366 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4367 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4368 mp_ps_print_cmd(mp, "curveto","c");
4370 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4371 mp_ps_print_cmd(mp, "lineto","l");
4374 @ Two types of straight lines come up often in \MP\ paths:
4375 cubics with zero initial and final velocity as created by |make_path| or
4376 |make_envelope|, and cubics with control points uniformly spaced on a line
4377 as created by |make_choices|.
4379 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4381 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4382 if ( gr_right_x(p)==gr_x_coord(p) )
4383 if ( gr_right_y(p)==gr_y_coord(p) )
4384 if ( gr_left_x(q)==gr_x_coord(q) )
4385 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4386 d=gr_left_x(q)-gr_right_x(p);
4387 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4388 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4389 { d=gr_left_y(q)-gr_right_y(p);
4390 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4391 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4394 @ The colored objects use a struct with anonymous fields to express the color parts:
4398 scaled _a_val, _b_val, _c_val, _d_val;
4401 @ The exported form of a dash pattern is simpler than the internal
4402 format, it is closely modelled to the PostScript model. The array of
4403 dashes is ended by a single negative value, because this is not
4404 allowed in PostScript.
4408 scaled offset_field;
4409 scaled *array_field;
4414 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
4417 void mp_do_gr_toss_dashes(mp_dash_object *dl);
4420 void mp_do_gr_toss_dashes(mp_dash_object *dl) {
4423 mp_xfree(dl->array_field);
4429 mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) {
4430 mp_dash_object *q = NULL;
4434 q = mp_xmalloc(mp, 1, sizeof (mp_dash_object));
4435 memcpy (q,dl,sizeof(mp_dash_object));
4436 if (dl->array_field != NULL) {
4438 while (*(dl->array_field+i) != -1) i++;
4439 q->array_field = mp_xmalloc(mp, i, sizeof (scaled));
4440 memcpy(q->array_field,dl->array_field, (i*sizeof(scaled)));
4446 @ Now for outputting the actual graphic objects. First, set up some
4447 structures and access macros.
4449 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4452 #define gr_type(A) (A)->_type_field
4453 #define gr_link(A) (A)->_link_field
4454 #define gr_color_model(A) (A)->color_model_field
4455 #define gr_red_val(A) (A)->color_field._a_val
4456 #define gr_green_val(A) (A)->color_field._b_val
4457 #define gr_blue_val(A) (A)->color_field._c_val
4458 #define gr_cyan_val(A) (A)->color_field._a_val
4459 #define gr_magenta_val(A) (A)->color_field._b_val
4460 #define gr_yellow_val(A) (A)->color_field._c_val
4461 #define gr_black_val(A) (A)->color_field._d_val
4462 #define gr_grey_val(A) (A)->color_field._a_val
4463 #define gr_path_p(A) (A)->path_p_field
4464 #define gr_htap_p(A) ((mp_fill_object *)A)->htap_p_field
4465 #define gr_pen_p(A) (A)->pen_p_field
4466 #define gr_ljoin_val(A) (A)->ljoin_field
4467 #define gr_lcap_val(A) ((mp_stroked_object *)A)->lcap_field
4468 #define gr_miterlim_val(A) (A)->miterlim_field
4469 #define gr_pre_script(A) (A)->pre_script_field
4470 #define gr_post_script(A) (A)->post_script_field
4471 #define gr_dash_p(A) ((mp_stroked_object *)A)->dash_p_field
4472 #define gr_name_type(A) ((mp_text_object *)A)->name_type_field
4473 #define gr_text_p(A) ((mp_text_object *)A)->text_p_field
4474 #define gr_font_n(A) ((mp_text_object *)A)->font_n_field
4475 #define gr_font_name(A) ((mp_text_object *)A)->font_name_field
4476 #define gr_font_dsize(A) ((mp_text_object *)A)->font_dsize_field
4477 #define gr_width_val(A) ((mp_text_object *)A)->width_field
4478 #define gr_height_val(A) ((mp_text_object *)A)->height_field
4479 #define gr_depth_val(A) ((mp_text_object *)A)->depth_field
4480 #define gr_tx_val(A) ((mp_text_object *)A)->tx_field
4481 #define gr_ty_val(A) ((mp_text_object *)A)->ty_field
4482 #define gr_txx_val(A) ((mp_text_object *)A)->txx_field
4483 #define gr_txy_val(A) ((mp_text_object *)A)->txy_field
4484 #define gr_tyx_val(A) ((mp_text_object *)A)->tyx_field
4485 #define gr_tyy_val(A) ((mp_text_object *)A)->tyy_field
4487 #define GRAPHIC_BODY \
4488 halfword _type_field; \
4489 struct mp_graphic_object * _link_field
4491 typedef struct mp_graphic_object {
4493 } mp_graphic_object;
4495 typedef struct mp_text_object {
4497 char *pre_script_field;
4498 char *post_script_field;
4499 mp_color color_field;
4500 quarterword color_model_field;
4501 quarterword name_type_field;
4503 char *font_name_field ;
4504 scaled font_dsize_field ;
4505 font_number font_n_field ;
4506 scaled width_field ;
4507 scaled height_field ;
4508 scaled depth_field ;
4517 typedef struct mp_fill_object {
4519 char *pre_script_field;
4520 char *post_script_field;
4521 mp_color color_field;
4522 quarterword color_model_field;
4523 quarterword ljoin_field ;
4524 mp_knot * path_p_field;
4525 mp_knot * htap_p_field;
4526 mp_knot * pen_p_field;
4527 scaled miterlim_field ;
4530 typedef struct mp_stroked_object {
4532 char *pre_script_field;
4533 char *post_script_field;
4534 mp_color color_field;
4535 quarterword color_model_field;
4536 quarterword ljoin_field ;
4537 quarterword lcap_field ;
4538 mp_knot * path_p_field;
4539 mp_knot * pen_p_field;
4540 scaled miterlim_field ;
4541 mp_dash_object *dash_p_field;
4542 } mp_stroked_object;
4544 typedef struct mp_clip_object {
4546 mp_knot * path_p_field;
4549 typedef struct mp_bounds_object {
4551 mp_knot * path_p_field;
4554 typedef struct mp_special_object {
4556 char *pre_script_field;
4557 } mp_special_object ;
4559 typedef struct mp_edge_object {
4560 struct mp_graphic_object * body;
4561 struct mp_edge_object * _next;
4564 scaled _minx, _miny, _maxx, _maxy;
4567 @ @<Exported function headers@>=
4568 mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4571 mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4572 mp_graphic_object *p;
4575 case mp_fill_code: size = sizeof(mp_fill_object); break;
4576 case mp_stroked_code: size = sizeof(mp_stroked_object); break;
4577 case mp_text_code: size = sizeof(mp_text_object); break;
4578 case mp_start_clip_code: size = sizeof(mp_clip_object); break;
4579 case mp_start_bounds_code: size = sizeof(mp_bounds_object); break;
4580 case mp_special_code: size = sizeof(mp_special_object); break;
4581 default: size = sizeof(mp_graphic_object); break;
4583 p = (mp_graphic_object *)mp_xmalloc(mp,1,size);
4589 @ We need to keep track of several parameters from the \ps\ graphics state.
4591 This allows us to be sure that \ps\ has the correct values when they are
4592 needed without wasting time and space setting them unnecessarily.
4594 @d gs_red mp->ps->gs_state->red_field
4595 @d gs_green mp->ps->gs_state->green_field
4596 @d gs_blue mp->ps->gs_state->blue_field
4597 @d gs_black mp->ps->gs_state->black_field
4598 @d gs_colormodel mp->ps->gs_state->colormodel_field
4599 @d gs_ljoin mp->ps->gs_state->ljoin_field
4600 @d gs_lcap mp->ps->gs_state->lcap_field
4601 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4602 @d gs_miterlim mp->ps->gs_state->miterlim_field
4603 @d gs_dash_p mp->ps->gs_state->dash_p_field
4604 @d gs_dash_init_done mp->ps->gs_state->dash_done_field
4605 @d gs_previous mp->ps->gs_state->previous_field
4606 @d gs_width mp->ps->gs_state->width_field
4609 typedef struct _gs_state {
4611 scaled green_field ;
4613 scaled black_field ;
4614 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4615 quarterword colormodel_field ;
4616 /* the current colormodel */
4617 quarterword ljoin_field ;
4618 quarterword lcap_field ;
4619 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4620 quarterword adj_wx_field ;
4621 /* what resolution-dependent adjustment applies to the width */
4622 scaled miterlim_field ;
4623 /* the value from the last \&{setmiterlimit} command */
4624 mp_dash_object * dash_p_field ;
4625 /* edge structure for last \&{setdash} command */
4626 boolean dash_done_field ; /* to test for initial \&{setdash} */
4627 struct _gs_state * previous_field ;
4628 /* backlink to the previous |_gs_state| structure */
4629 scaled width_field ;
4630 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4635 struct _gs_state * gs_state;
4638 mp->ps->gs_state=NULL;
4640 @ @<Dealloc variables@>=
4641 mp_xfree(mp->ps->gs_state);
4643 @ To avoid making undue assumptions about the initial graphics state, these
4644 parameters are given special values that are guaranteed not to match anything
4645 in the edge structure being shipped out. On the other hand, the initial color
4646 should be black so that the translation of an all-black picture will have no
4647 \&{setcolor} commands. (These would be undesirable in a font application.)
4648 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4649 to recover from a situation where we have lost track of the graphics state.
4652 void mp_gs_unknown_graphics_state (MP mp,scaled c) ;
4655 @d mp_void (null+1) /* a null pointer different from |null| */
4657 @c void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4658 struct _gs_state *p; /* to shift graphic states around */
4659 if ( (c==0)||(c==-1) ) {
4660 if ( mp->ps->gs_state==NULL ) {
4661 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4664 while ( gs_previous!=NULL ) {
4666 mp_xfree(mp->ps->gs_state);
4670 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4671 gs_colormodel=mp_uninitialized_model;
4676 gs_dash_init_done=false;
4678 } else if ( c==1 ) {
4679 p= mp->ps->gs_state;
4680 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4681 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4683 } else if ( c==2 ) {
4685 mp_xfree(mp->ps->gs_state);
4691 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4692 that \ps's idea of the graphics state agrees with what is stored in the object.
4695 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ;
4698 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) {
4699 /* get ready to output graphical object |p| */
4700 mp_knot *pp, *path_p; /* for list manipulation */
4702 scaled wx,wy,ww; /* dimensions of pen bounding box */
4703 boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4704 integer tx,ty; /* temporaries for computing |adj_wx| */
4705 if ( gr_has_color(p) )
4706 @<Make sure \ps\ will use the right color for object~|p|@>;
4707 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) {
4708 if (gr_type(p)==mp_fill_code) {
4709 pp = gr_pen_p((mp_fill_object *)p);
4710 path_p = gr_path_p((mp_fill_object *)p);
4712 pp = gr_pen_p((mp_stroked_object *)p);
4713 path_p = gr_path_p((mp_stroked_object *)p);
4716 if ( pen_is_elliptical(pp) ) {
4717 @<Generate \ps\ code that sets the stroke width to the
4718 appropriate rounded value@>;
4719 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4720 @<Decide whether the line cap parameter matters and set it if necessary@>;
4721 @<Set the other numeric parameters as needed for object~|p|@>;
4724 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
4727 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4728 if ( gr_type(p)==mp_stroked_code ) {
4729 mp_stroked_object *ts = (mp_stroked_object *)p;
4730 if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) )
4731 if ( gs_lcap!=gr_lcap_val(ts) ) {
4733 mp_ps_print_char(mp, ' ');
4734 mp_ps_print_char(mp, '0'+gr_lcap_val(ts));
4735 mp_ps_print_cmd(mp, " setlinecap"," lc");
4736 gs_lcap=gr_lcap_val(ts);
4741 @d set_ljoin_miterlim(p)
4742 if ( gs_ljoin!=gr_ljoin_val(p) ) {
4744 mp_ps_print_char(mp, ' ');
4745 mp_ps_print_char(mp, '0'+gr_ljoin_val(p));
4746 mp_ps_print_cmd(mp, " setlinejoin"," lj");
4747 gs_ljoin=gr_ljoin_val(p);
4749 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4751 mp_ps_print_char(mp, ' ');
4752 mp_ps_print_scaled(mp, gr_miterlim_val(p));
4753 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4754 gs_miterlim=gr_miterlim_val(p);
4757 @<Set the other numeric parameters as needed for object~|p|@>=
4758 if ( gr_type(p)==mp_stroked_code ) {
4759 mp_stroked_object *ts = (mp_stroked_object *)p;
4760 set_ljoin_miterlim(ts);
4762 mp_fill_object *ts = (mp_fill_object *)p;
4763 set_ljoin_miterlim(ts);
4767 @d set_color_objects(pq)
4768 object_color_model = pq->color_model_field;
4769 object_color_a = pq->color_field._a_val;
4770 object_color_b = pq->color_field._b_val;
4771 object_color_c = pq->color_field._c_val;
4772 object_color_d = pq->color_field._d_val;
4774 @<Make sure \ps\ will use the right color for object~|p|@>=
4776 int object_color_model;
4777 int object_color_a, object_color_b, object_color_c, object_color_d ;
4778 if (gr_type(p) == mp_fill_code) {
4779 mp_fill_object *pq = (mp_fill_object *)p;
4780 set_color_objects(pq);
4781 } else if (gr_type(p) == mp_stroked_code) {
4782 mp_stroked_object *pq = (mp_stroked_object *)p;
4783 set_color_objects(pq);
4785 mp_text_object *pq = (mp_text_object *)p;
4786 set_color_objects(pq);
4789 if ( object_color_model==mp_rgb_model) {
4790 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)||
4791 (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) {
4792 gs_red = object_color_a;
4793 gs_green = object_color_b;
4794 gs_blue = object_color_c;
4796 gs_colormodel=mp_rgb_model;
4798 mp_ps_print_char(mp, ' ');
4799 mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
4800 mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
4801 mp_ps_print_scaled(mp, gs_blue);
4802 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4805 } else if ( object_color_model==mp_cmyk_model) {
4806 if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)||
4807 (gs_blue!=object_color_c)||(gs_black!=object_color_d)||
4808 (gs_colormodel!=mp_cmyk_model) ) {
4809 gs_red = object_color_a;
4810 gs_green = object_color_b;
4811 gs_blue = object_color_c;
4812 gs_black = object_color_d;
4813 gs_colormodel=mp_cmyk_model;
4815 mp_ps_print_char(mp, ' ');
4816 mp_ps_print_scaled(mp, gs_red);
4817 mp_ps_print_char(mp, ' ');
4818 mp_ps_print_scaled(mp, gs_green);
4819 mp_ps_print_char(mp, ' ');
4820 mp_ps_print_scaled(mp, gs_blue);
4821 mp_ps_print_char(mp, ' ');
4822 mp_ps_print_scaled(mp, gs_black);
4823 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4826 } else if ( object_color_model==mp_grey_model ) {
4827 if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) {
4828 gs_red = object_color_a;
4832 gs_colormodel=mp_grey_model;
4834 mp_ps_print_char(mp, ' ');
4835 mp_ps_print_scaled(mp, gs_red);
4836 mp_ps_print_cmd(mp, " setgray"," G");
4839 } else if ( object_color_model==mp_no_model ) {
4840 gs_colormodel=mp_no_model;
4844 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4845 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4846 @:setwidth}{\&{setwidth}command@>
4847 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4848 generate \ps\ code that computes the rounded value. For non-circular pens, the
4849 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4850 have the computed width.
4852 Rounding the width to whole pixels is not likely to improve the appearance of
4853 diagonal or curved strokes, but we do it anyway for consistency. The
4854 \&{truncate} command generated here tends to make all the strokes a little
4855 @:truncate}{\&{truncate} command@>
4856 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4857 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4858 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4861 @<Generate \ps\ code that sets the stroke width...@>=
4862 @<Set |wx| and |wy| to the width and height of the bounding box for
4864 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4865 important and set |adj_wx| and |ww| accordingly@>;
4866 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4869 mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
4871 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4873 if ( mp->internal[mp_procset]>0 ) {
4875 mp_ps_print_char(mp, ' ');
4876 mp_ps_print_scaled(mp, ww);
4877 mp_ps_print(mp, " vlw");
4880 mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
4881 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4888 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4889 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4890 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4891 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4893 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4894 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4897 @ The path is considered ``essentially horizontal'' if its range of
4898 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4899 vertical'' paths are detected similarly. This code ensures that no component
4900 of the pen transformation is more that |aspect_bound*(ww+1)|.
4902 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4903 more than this factor */
4908 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4910 if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound;
4911 else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound;
4912 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
4913 else { ww=wx; adj_wx=true; }
4915 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4916 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4917 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4918 bounding-box computation.
4921 boolean mp_gr_coord_rangeOK (mp_knot *h,
4922 small_number zoff, scaled dz);
4925 boolean mp_gr_coord_rangeOK (mp_knot *h,
4926 small_number zoff, scaled dz) {
4927 mp_knot *p; /* for scanning the path form |h| */
4928 scaled zlo,zhi; /* coordinate range so far */
4929 scaled z; /* coordinate currently being tested */
4930 if (zoff==do_x_loc) {
4934 while ( gr_right_type(p)!=mp_endpoint ) {
4936 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4937 p=gr_next_knot(p); z=gr_left_x(p);
4938 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4940 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4947 while ( gr_right_type(p)!=mp_endpoint ) {
4949 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4950 p=gr_next_knot(p); z=gr_left_y(p);
4951 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4953 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4960 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4962 else if ( z>zhi ) zhi=z;
4963 if ( zhi-zlo>dz ) return false
4965 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4966 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4967 @:stroke}{\&{stroke} command@>
4968 @:fill}{\&{fill} command@>
4969 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4972 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4973 if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) {
4979 if ( gs_dash_p!=NULL || gs_dash_init_done == false) {
4980 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4982 gs_dash_init_done=true;
4984 } else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) {
4985 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
4989 scaled mp_gr_get_pen_scale (MP mp, mp_knot *p) ;
4993 scaled mp_gr_get_pen_scale (MP mp, mp_knot *p) {
4994 return mp_sqrt_det(mp,
4995 gr_left_x(p)-gr_x_coord(p), gr_right_x(p)-gr_x_coord(p),
4996 gr_left_y(p)-gr_y_coord(p), gr_right_y(p)-gr_y_coord(p));
5000 @ The original code had a check here to ensure that the result from
5001 |mp_take_scaled| did not go out of bounds.
5003 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5005 if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array_field==NULL)) {
5006 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5010 mp_ps_print(mp, " [");
5011 for (i=0; *(hh->array_field+i) != -1;i++) {
5013 mp_ps_print_scaled(mp, *(hh->array_field+i));
5014 mp_ps_print_char(mp, ' ') ;
5017 mp_ps_print(mp, "] ");
5018 mp_ps_print_scaled(mp, hh->offset_field);
5019 mp_ps_print_cmd(mp, " setdash"," sd");
5024 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ;
5026 @ This function test if |h| and |hh| represent the same dash pattern.
5029 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) {
5032 if ( h==hh ) ret=true;
5033 else if ( (h==NULL)||(hh==NULL) ) ret=false;
5034 else if ( h->offset_field!=hh->offset_field ) ret=false;
5035 else if ( h->array_field == hh->array_field) ret=true;
5036 else if ( h->array_field == NULL || hh->array_field == NULL) ret=false;
5037 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5041 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5043 while (*(h->array_field+i)!=-1 &&
5044 *(hh->array_field+i)!=-1 &&
5045 *(h->array_field+i) == *(hh->array_field+i)) i++;
5047 if (*(h->array_field+(i))==-1 && *(hh->array_field+(i)) == -1)
5052 @ When stroking a path with an elliptical pen, it is necessary to transform
5053 the coordinate system so that a unit circular pen will have the desired shape.
5054 To keep this transformation local, we enclose it in a
5055 $$\&{gsave}\ldots\&{grestore}$$
5056 block. Any translation component must be applied to the path being stroked
5057 while the rest of the transformation must apply only to the pen.
5058 If |fill_also=true|, the path is to be filled as well as stroked so we must
5059 insert commands to do this after giving the path.
5062 void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) ;
5065 @c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) {
5066 /* generate an elliptical pen stroke from object |h| */
5067 scaled txx,txy,tyx,tyy; /* transformation parameters */
5068 mp_knot *p; /* the pen to stroke with */
5069 scaled d1,det; /* for tweaking transformation parameters */
5070 integer s; /* also for tweaking transformation paramters */
5071 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5073 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5075 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5076 if (gr_type(h)==mp_fill_code) {
5077 mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h));
5079 mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h));
5081 if ( mp->internal[mp_procset]==0 ) {
5082 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5083 @<Issue \ps\ commands to transform the coordinate system@>;
5084 mp_ps_print(mp, " stroke");
5085 if ( transformed ) mp_ps_print(mp, " grestore");
5087 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5088 if ( (txy!=0)||(tyx!=0) ) {
5089 mp_ps_print(mp, " [");
5090 mp_ps_pair_out(mp, txx,tyx);
5091 mp_ps_pair_out(mp, txy,tyy);
5092 mp_ps_print(mp, "0 0] t");
5093 } else if ((txx!=unity)||(tyy!=unity) ) {
5094 mp_ps_pair_out(mp,txx,tyy);
5095 mp_ps_print(mp, " s");
5097 mp_ps_print(mp, " S");
5098 if ( transformed ) mp_ps_print(mp, " Q");
5103 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5104 if (gr_type(h)==mp_fill_code) {
5105 p=gr_pen_p((mp_fill_object *)h);
5107 p=gr_pen_p((mp_stroked_object *)h);
5113 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5114 mp_ps_print_nl(mp, "");
5115 mp_ps_print_cmd(mp, "gsave ","q ");
5116 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5117 mp_ps_print(mp, "translate ");
5124 mp_ps_print_nl(mp, "");
5126 @<Adjust the transformation to account for |gs_width| and output the
5127 initial \&{gsave} if |transformed| should be |true|@>
5129 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5130 if ( gs_width!=unity ) {
5131 if ( gs_width==0 ) {
5132 txx=unity; tyy=unity;
5134 txx=mp_make_scaled(mp, txx,gs_width);
5135 txy=mp_make_scaled(mp, txy,gs_width);
5136 tyx=mp_make_scaled(mp, tyx,gs_width);
5137 tyy=mp_make_scaled(mp, tyy,gs_width);
5140 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5141 if ( (! transformed) ){
5142 mp_ps_print_cmd(mp, "gsave ","q ");
5147 @ @<Issue \ps\ commands to transform the coordinate system@>=
5148 if ( (txy!=0)||(tyx!=0) ){
5150 mp_ps_print_char(mp, '[');
5151 mp_ps_pair_out(mp, txx,tyx);
5152 mp_ps_pair_out(mp, txy,tyy);
5153 mp_ps_print(mp, "0 0] concat");
5154 } else if ( (txx!=unity)||(tyy!=unity) ){
5156 mp_ps_pair_out(mp, txx,tyy);
5157 mp_ps_print(mp, "scale");
5160 @ The \ps\ interpreter will probably abort if it encounters a singular
5161 transformation matrix. The determinant must be large enough to ensure that
5162 the printed representation will be nonsingular. Since the printed
5163 representation is always within $2^{-17}$ of the internal |scaled| value, the
5164 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5165 the magnitudes of |txx/65536|, |txy/65536|, etc.
5167 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5168 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5170 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5171 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5172 d1=4*aspect_bound+1;
5173 if ( abs(det)<d1 ) {
5174 if ( det>=0 ) { d1=d1-det; s=1; }
5175 else { d1=-d1-det; s=-1; };
5177 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5178 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5179 else txx=txx+(d1+s*abs(tyy)) / tyy;
5181 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5182 else txy=txy+(d1+s*abs(tyx)) / tyx;
5186 @ Here is a simple routine that just fills a cycle.
5189 void mp_gr_ps_fill_out (MP mp, mp_knot *p);
5192 void mp_gr_ps_fill_out (MP mp, mp_knot *p) { /* fill cyclic path~|p| */
5193 mp_gr_ps_path_out(mp, p);
5194 mp_ps_print_cmd(mp, " fill"," F");
5198 @ A text node may specify an arbitrary transformation but the usual case
5199 involves only shifting, scaling, and occasionally rotation. The purpose
5200 of |choose_scale| is to select a scale factor so that the remaining
5201 transformation is as ``nice'' as possible. The definition of ``nice''
5202 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5203 nice because they work out well for bitmap fonts. The code here selects
5204 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5205 non-shifting part of the transformation matrix. It is careful to avoid
5206 additions that might cause undetected overflow.
5209 scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) ;
5211 @ @c scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) {
5212 /* |p| should point to a text node */
5213 scaled a,b,c,d,ad,bc; /* temporary values */
5218 if ( a<0 ) negate(a);
5219 if ( b<0 ) negate(b);
5220 if ( c<0 ) negate(c);
5221 if ( d<0 ) negate(d);
5224 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5227 @ The potential overflow here is caused by the fact the returned value
5228 has to fit in a |name_type|, which is a quarterword.
5230 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
5233 quarterword mp_size_index (MP mp, font_number f, scaled s) ;
5236 quarterword mp_size_index (MP mp, font_number f, scaled s) {
5237 pointer p,q; /* the previous and current font size nodes */
5238 quarterword i; /* the size index for |q| */
5239 q=mp->font_sizes[f];
5242 if ( abs(s-sc_factor(q))<=fscale_tolerance )
5245 { p=q; q=link(q); incr(i); };
5246 if ( i==max_quarterword )
5247 mp_overflow(mp, "sizes per font",max_quarterword);
5248 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
5250 q=mp_get_node(mp, font_size_size);
5252 if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
5257 scaled mp_indexed_size (MP mp,font_number f, quarterword j);
5260 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
5261 pointer p; /* a font size node */
5262 quarterword i; /* the size index for |p| */
5263 p=mp->font_sizes[f];
5265 if ( p==null ) mp_confusion(mp, "size");
5268 if ( p==null ) mp_confusion(mp, "size");
5270 return sc_factor(p);
5274 void mp_clear_sizes (MP mp) ;
5276 @ @c void mp_clear_sizes (MP mp) {
5277 font_number f; /* the font whose size list is being cleared */
5278 pointer p; /* current font size nodes */
5279 for (f=null_font+1;f<=mp->last_fnum;f++) {
5280 while ( mp->font_sizes[f]!=null ) {
5281 p=mp->font_sizes[f];
5282 mp->font_sizes[f]=link(p);
5283 mp_free_node(mp, p,font_size_size);
5288 @ A text node may specify an arbitrary transformation but the usual case
5289 involves only shifting, scaling, and occasionally rotation. The purpose
5290 of |choose_scale| is to select a scale factor so that the remaining
5291 transformation is as ``nice'' as possible. The definition of ``nice''
5292 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5293 nice because they work out well for bitmap fonts. The code here selects
5294 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5295 non-shifting part of the transformation matrix. It is careful to avoid
5296 additions that might cause undetected overflow.
5298 @<Declare the \ps\ output procedures@>=
5299 scaled mp_choose_scale (MP mp, mp_graphic_object *p) ;
5301 @ @c scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
5302 /* |p| should point to a text node */
5303 scaled a,b,c,d,ad,bc; /* temporary values */
5308 if ( (a<0) ) negate(a);
5309 if ( (b<0) ) negate(b);
5310 if ( (c<0) ) negate(c);
5311 if ( (d<0) ) negate(d);
5314 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5317 @ There may be many sizes of one font and we need to keep track of the
5318 characters used for each size. This is done by keeping a linked list of
5319 sizes for each font with a counter in each text node giving the appropriate
5320 position in the size list for its font.
5322 @d font_size_size 2 /* size of a font size node */
5326 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size);
5329 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) {
5330 mp_graphic_object * p;
5332 while ( p!= NULL ) {
5333 if ( gr_type(p)==mp_text_code ) {
5334 if ( gr_font_n(p)!=null_font ) {
5335 if ( gr_name_type(p)==next_size )
5336 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p));
5343 @ @<Unmark all marked characters@>=
5344 for (f=null_font+1;f<=mp->last_fnum;f++) {
5345 if ( mp->font_sizes[f]!=null ) {
5346 mp_unmark_font(mp, f);
5347 mp->font_sizes[f]=null;
5351 @ @<Scan all the text nodes and mark the used ...@>=
5354 if ( gr_type(p)==mp_text_code ) {
5356 if (f!=null_font ) {
5357 switch (prologues) {
5360 mp->font_sizes[f] = mp_void;
5361 mp_mark_string_chars(mp, f, gr_text_p(p));
5362 if (mp_has_fm_entry(mp,f,NULL) ) {
5363 if (mp->font_enc_name[f]==NULL )
5364 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
5365 mp->font_ps_name[f] = mp_fm_font_name(mp,f);
5369 mp->font_sizes[f]=mp_void;
5372 gr_name_type(p)=mp_size_index(mp, f,mp_choose_scale(mp, p));
5373 if ( gr_name_type(p)==0 )
5374 mp_mark_string_chars(mp, f, gr_text_p(p));
5383 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5385 @<Exported function headers@>=
5386 void mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset) ;
5389 void mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset) {
5390 mp_graphic_object *p;
5391 scaled ds,scf; /* design size and scale factor for a text node */
5392 font_number f; /* for loops over fonts while (un)marking characters */
5393 boolean transformed; /* is the coordinate system being transformed? */
5394 MP mp = hh->_parent;
5395 if (mp->history >= mp_fatal_error_stop ) return;
5397 prologues = (mp->internal[mp_prologues]>>16);
5399 procset = (mp->internal[mp_procset]>>16);
5400 mp_open_output_file(mp);
5401 mp_print_initial_comment(mp, hh, prologues);
5403 @<Unmark all marked characters@>;
5404 if ( prologues==2 || prologues==3 ) {
5405 mp_reload_encodings(mp);
5407 @<Scan all the text nodes and mark the used characters@>;
5408 if ( prologues==2 || prologues==3 ) {
5409 mp_print_improved_prologue(mp, hh, prologues, procset);
5411 mp_print_prologue(mp, hh, prologues, procset);
5413 mp_gs_unknown_graphics_state(mp, 0);
5416 if ( gr_has_color(p) ) {
5417 @<Write |pre_script| of |p|@>;
5419 mp_gr_fix_graphics_state(mp, p);
5420 switch (gr_type(p)) {
5422 if ( gr_pen_p((mp_fill_object *)p)==NULL ) {
5423 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5424 } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) ) {
5425 mp_gr_stroke_ellipse(mp, p,true);
5427 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5428 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5430 if ( gr_post_script((mp_fill_object *)p)!=NULL ) {
5431 mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p));
5435 case mp_stroked_code:
5436 if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) )
5437 mp_gr_stroke_ellipse(mp, p,false);
5439 mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p));
5441 if ( gr_post_script((mp_stroked_object *)p)!=NULL ) {
5442 mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p));
5447 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5449 scf=mp_gr_choose_scale(mp, p);
5451 scf=mp_indexed_size(mp, gr_font_n(p), gr_name_type(p));
5452 @<Shift or transform as necessary before outputting text node~|p| at scale
5453 factor~|scf|; set |transformed:=true| if the original transformation must
5455 mp_ps_string_out(mp, gr_text_p(p));
5456 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5457 @<Print the size information and \ps\ commands for text node~|p|@>;
5460 if ( gr_post_script((mp_text_object *)p)!=NULL ) {
5461 mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp);
5464 case mp_start_clip_code:
5465 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5466 mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p));
5467 mp_ps_print_cmd(mp, " clip"," W");
5469 if ( mp->internal[mp_restore_clip_color]>0 )
5470 mp_gs_unknown_graphics_state(mp, 1);
5472 case mp_stop_clip_code:
5473 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5475 if ( mp->internal[mp_restore_clip_color]>0 )
5476 mp_gs_unknown_graphics_state(mp, 2);
5478 mp_gs_unknown_graphics_state(mp, -1);
5480 case mp_start_bounds_code:
5481 case mp_stop_bounds_code:
5483 case mp_special_code:
5485 mp_special_object *ps = (mp_special_object *)p;
5486 mp_ps_print_nl (mp, gr_pre_script(ps));
5487 mp_ps_print_ln (mp);
5490 } /* all cases are enumerated */
5493 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
5494 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
5495 (mp->close_file)(mp,mp->ps_file);
5501 @d do_write_prescript(a,b) {
5502 if ( (gr_pre_script((b *)a))!=NULL ) {
5503 mp_ps_print_nl (mp, gr_pre_script((b *)a));
5508 @<Write |pre_script| of |p|@>=
5510 if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); }
5511 else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); }
5512 else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); }
5515 @ The envelope of a cyclic path~|q| could be computed by calling
5516 |make_envelope| once for |q| and once for its reversal. We don't do this
5517 because it would fail color regions that are covered by the pen regardless
5518 of where it is placed on~|q|.
5520 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5521 if ( gr_left_type(q)!=mp_endpoint ) {
5522 gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5523 gr_right_type(q)=mp_endpoint;
5528 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5530 mp_ps_print_char(mp, ' ');
5531 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5532 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5533 mp_ps_print(mp, " fshow");
5535 mp_ps_print_cmd(mp, " grestore"," Q")
5539 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5540 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5541 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5542 if ( transformed ) {
5543 mp_ps_print_cmd(mp, "gsave [", "q [");
5544 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5545 mp_make_scaled(mp, gr_tyx_val(p),scf));
5546 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5547 mp_make_scaled(mp, gr_tyy_val(p),scf));
5548 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5549 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5551 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5552 mp_ps_print_cmd(mp, "moveto","m");
5557 @ @<Exported function headers@>=
5558 void mp_gr_toss_objects ( mp_edge_object *hh) ;
5559 void mp_gr_toss_object (mp_graphic_object *p) ;
5562 void mp_gr_toss_object (mp_graphic_object *p) {
5564 mp_stroked_object *ts;
5566 switch (gr_type(p)) {
5568 tf = (mp_fill_object *)p;
5569 mp_xfree(gr_pre_script(tf));
5570 mp_xfree(gr_post_script(tf));
5571 mp_gr_toss_knot_list(mp,gr_pen_p(tf));
5572 mp_gr_toss_knot_list(mp,gr_path_p(tf));
5573 mp_gr_toss_knot_list(mp,gr_htap_p(p));
5575 case mp_stroked_code:
5576 ts = (mp_stroked_object *)p;
5577 mp_xfree(gr_pre_script(ts));
5578 mp_xfree(gr_post_script(ts));
5579 mp_gr_toss_knot_list(mp,gr_pen_p(ts));
5580 mp_gr_toss_knot_list(mp,gr_path_p(ts));
5581 if (gr_dash_p(p)!=NULL)
5582 mp_gr_toss_dashes (mp,gr_dash_p(p));
5585 tt = (mp_text_object *)p;
5586 mp_xfree(gr_pre_script(tt));
5587 mp_xfree(gr_post_script(tt));
5588 mp_xfree(gr_text_p(p));
5589 mp_xfree(gr_font_name(p));
5591 case mp_start_clip_code:
5592 mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p));
5594 case mp_start_bounds_code:
5595 mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p));
5597 case mp_stop_clip_code:
5598 case mp_stop_bounds_code:
5600 case mp_special_code:
5601 mp_xfree(gr_pre_script((mp_special_object *)p));
5603 } /* all cases are enumerated */
5609 void mp_gr_toss_objects (mp_edge_object *hh) {
5610 mp_graphic_object *p, *q;
5614 mp_gr_toss_object(p);
5620 @ @<Exported function headers@>=
5621 mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ;
5625 mp_gr_copy_object (MP mp, mp_graphic_object *p) {
5627 mp_stroked_object *ts;
5630 mp_bounds_object *tb;
5631 mp_special_object *tp;
5632 mp_graphic_object *q = NULL;
5633 switch (gr_type(p)) {
5635 tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code);
5636 gr_pre_script(tf) = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p));
5637 gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p));
5638 gr_path_p(tf) = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p));
5639 gr_htap_p(tf) = mp_gr_copy_path(mp,gr_htap_p(p));
5640 gr_pen_p(tf) = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p));
5641 q = (mp_graphic_object *)tf;
5643 case mp_stroked_code:
5644 ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code);
5645 gr_pre_script(ts) = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p));
5646 gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p));
5647 gr_path_p(ts) = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p));
5648 gr_pen_p(ts) = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p));
5649 gr_dash_p(ts) = mp_gr_copy_dashes(mp,gr_dash_p(p));
5650 q = (mp_graphic_object *)ts;
5653 tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code);
5654 gr_pre_script(tt) = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p));
5655 gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p));
5656 gr_text_p(tt) = mp_xstrdup(mp, gr_text_p(p));
5657 gr_font_name(tt) = mp_xstrdup(mp, gr_font_name(p));
5658 q = (mp_graphic_object *)tt;
5660 case mp_start_clip_code:
5661 tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code);
5662 gr_path_p(tc) = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p));
5663 q = (mp_graphic_object *)tc;
5665 case mp_start_bounds_code:
5666 tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code);
5667 gr_path_p(tb) = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p));
5668 q = (mp_graphic_object *)tb;
5670 case mp_special_code:
5671 tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code);
5672 gr_pre_script(tp) = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p));
5673 q = (mp_graphic_object *)tp;
5675 case mp_stop_clip_code:
5676 q = mp_new_graphic_object(mp, mp_stop_clip_code);
5678 case mp_stop_bounds_code:
5679 q = mp_new_graphic_object(mp, mp_stop_bounds_code);
5681 } /* all cases are enumerated */