3 % Copyright 2008 Taco Hoekwater.
5 % This program is free software: you can redistribute it and/or modify
6 % it under the terms of the GNU General Public License as published by
7 % the Free Software Foundation, either version 2 of the License, or
8 % (at your option) any later version.
10 % This program is distributed in the hope that it will be useful,
11 % but WITHOUT ANY WARRANTY; without even the implied warranty of
12 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 % GNU General Public License for more details.
15 % You should have received a copy of the GNU General Public License
16 % along with this program. If not, see <http://www.gnu.org/licenses/>.
18 % TeX is a trademark of the American Mathematical Society.
19 % METAFONT is a trademark of Addison-Wesley Publishing Company.
20 % PostScript is a trademark of Adobe Systems Incorporated.
22 % Here is TeX material that gets inserted after \input webmac
23 \def\hang{\hangindent 3em\noindent\ignorespaces}
24 \def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
27 \def\ph{\hbox{Pascal-H}}
28 \def\psqrt#1{\sqrt{\mathstrut#1}}
30 \def\pct!{{\char`\%}} % percent sign in ordinary text
31 \font\tenlogo=logo10 % font used for the METAFONT logo
33 \def\MF{{\tenlogo META}\-{\tenlogo FONT}}
34 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
35 \def\<#1>{$\langle#1\rangle$}
36 \def\section{\mathhexbox278}
37 \let\swap=\leftrightarrow
38 \def\round{\mathop{\rm round}\nolimits}
39 \mathchardef\vb="026A % synonym for `\|'
40 \def\[#1]{} % from pascal web
41 \def\(#1){} % this is used to make section names sort themselves better
42 \def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
44 \let\?=\relax % we want to be able to \write a \?
46 \def\title{MetaPost \ps\ output}
47 \def\topofcontents{\hsize 5.5in
48 \vglue -30pt plus 1fil minus 1.5in
49 \def\?##1]{\hbox to 1in{\hfil##1.\ }}
51 \def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
60 @d unity 0200000 /* $2^{16}$, represents 1.00000 */
61 @d el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
62 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
63 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
64 @d negate(A) (A)=-(A) /* change the sign of a variable */
67 @d print_err(A) mp_print_err(mp,(A))
68 @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
78 #include "psout.h" /* external header */
79 #include "mpmp.h" /* internal header */
80 #include "mppsout.h" /* internal header */
83 @<Static variables in the outer block@>
85 @ There is a small bit of code from the backend that bleads through
86 to the frontend because I do not know how to set up the includes
87 properly. Those are the definitions of |struct libavl_allocator|
88 and |typedef struct psout_data_struct * psout_data|.
90 The |libavl_allocator| is a trick that makes sure that frontends
91 do not need |avl.h|, and the |psout_data| is needed for the backend
96 typedef struct psout_data_struct {
99 @<Exported function headers@>
102 static boolean mp_isdigit (int a) {
103 return (a>='0'&&a<='9');
105 static int mp_tolower (int a) {
106 if (a>='A' && a <='Z')
107 return a - 'A' + 'a';
110 static int mp_strcasecmp (const char *s1, const char *s2) {
116 *c = mp_tolower(*c); c++;
121 *c = mp_tolower(*c); c++;
124 free (ss1); free(ss2);
128 @ @<Exported function headers@>=
129 void mp_backend_initialize (MP mp) ;
130 void mp_backend_free (MP mp) ;
133 @c void mp_backend_initialize (MP mp) {
134 mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
135 @<Set initial values@>;
137 void mp_backend_free (MP mp) {
138 @<Dealloc variables@>;
146 @ Writing to ps files
150 /* the number of characters on the current \ps\ file line */
152 @ @<Set initial values@>=
153 mp->ps->ps_offset = 0;
157 @d wps(A) (mp->write_ascii_file)(mp,mp->ps_file,(A))
161 (mp->write_ascii_file)(mp,mp->ps_file,(char *)ss);
163 @d wps_cr (mp->write_ascii_file)(mp,mp->ps_file,"\n")
164 @d wps_ln(A) { wterm_cr; (mp->write_ascii_file)(mp,mp->ps_file,(A)); }
167 static void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
173 static void mp_ps_print_char (MP mp, int s) { /* prints a single character */
175 wps_cr; mp->ps->ps_offset=0;
177 wps_chr(s); incr(mp->ps->ps_offset);
182 static void mp_ps_do_print (MP mp, const char *ss, size_t len) { /* prints string |s| */
185 mp_ps_print_char(mp, ss[j]); incr(j);
189 @ Deciding where to break the ps output line.
191 @d ps_room(A) if ( (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) {
192 mp_ps_print_ln(mp); /* optional line break */
196 static void mp_ps_print (MP mp, const char *ss) {
198 mp_ps_do_print(mp, ss, strlen(ss));
201 @ The procedure |print_nl| is like |print|, but it makes sure that the
202 string appears at the beginning of a new line.
205 static void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */
206 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
210 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
213 static void mp_ps_print_the_digs (MP mp, int k) {
214 /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
216 mp_ps_print_char(mp, '0'+mp->dig[k]);
220 @ The following procedure, which prints out the decimal representation of a
221 given integer |n|, has been written carefully so that it works properly
222 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
223 to negative arguments, since such operations are not implemented consistently
224 by all \PASCAL\ compilers.
227 static void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */
228 integer m; /* used to negate |n| in possibly dangerous cases */
229 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
231 mp_ps_print_char(mp, '-');
232 if ( n>-100000000 ) {
235 m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
237 mp->dig[0]=(unsigned char)m;
239 mp->dig[0]=0; incr(n);
244 mp->dig[k]=(unsigned char)n % 10; n=n / 10; incr(k);
246 mp_ps_print_the_digs(mp, k);
250 void mp_ps_print_int (MP mp,integer n);
252 @ \MP\ also makes use of a trivial procedure to print two digits. The
253 following subroutine is usually called with a parameter in the range |0<=n<=99|.
256 static void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */
258 mp_ps_print_char(mp, '0'+(n / 10));
259 mp_ps_print_char(mp, '0'+(n % 10));
262 @ Conversely, here is a procedure analogous to |print_int|. If the output
263 of this procedure is subsequently read by \MP\ and converted by the
264 |round_decimals| routine above, it turns out that the original value will
265 be reproduced exactly. A decimal point is printed only if the value is
266 not an integer. If there is more than one way to print the result with
267 the optimum number of digits following the decimal point, the closest
268 possible value is given.
270 The invariant relation in the \&{repeat} loop is that a sequence of
271 decimal digits yet to be printed will yield the original number if and only if
272 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
273 We can stop if and only if $f=0$ satisfies this condition; the loop will
274 terminate before $s$ can possibly become zero.
277 static void mp_ps_print_scaled (MP mp,scaled s) {
278 scaled delta; /* amount of allowable inaccuracy */
280 mp_ps_print_char(mp, '-');
281 negate(s); /* print the sign, if negative */
283 mp_ps_print_int(mp, s / unity); /* print the integer part */
287 mp_ps_print_char(mp, '.');
290 s=s+0100000-(delta / 2); /* round the final digit */
291 mp_ps_print_char(mp, '0'+(s / unity));
299 @* \[44a] Dealing with font encodings.
301 First, here are a few helpers for parsing files
303 @d check_buf(size, buf_size)
304 if ((unsigned)(size) > (unsigned)(buf_size)) {
306 mp_snprintf(S,128,"buffer overflow: (%d,%d) at file %s, line %d",
307 size,buf_size, __FILE__, __LINE__ );
308 mp_fatal_error(mp,S);
311 @d append_char_to_buf(c, p, buf, buf_size) do {
314 if (c == 13 || c == EOF)
316 if (c != ' ' || (p > buf && p[-1] != 32)) {
317 check_buf(p - buf + 1, (buf_size));
322 @d append_eol(p, buf, buf_size) do {
323 check_buf(p - buf + 2, (buf_size));
324 if (p - buf > 1 && p[-1] != 10)
326 if (p - buf > 2 && p[-2] == 32) {
333 @d remove_eol(p, buf) do {
339 @d skip(p, c) if (*p == c) p++
340 @d strend(s) strchr(s, 0)
341 @d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
346 boolean loaded; /* the encoding has been loaded? */
347 char *file_name; /* encoding file name */
348 char *enc_name; /* encoding true name */
349 integer objnum; /* object number */
351 integer tounicode; /* object number of associated ToUnicode entry */
361 #define ENC_BUF_SIZE 0x1000
362 char enc_line[ENC_BUF_SIZE];
366 @d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file)
367 @d enc_close() (mp->close_file)(mp,mp->ps->enc_file)
370 static int enc_getchar(MP mp) {
372 unsigned char abyte=0;
373 void *byte_ptr = &abyte;
374 (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len);
379 static boolean mp_enc_open (MP mp, char *n) {
380 mp->ps->enc_file=(mp->open_file)(mp,n, "r", mp_filetype_encoding);
381 if (mp->ps->enc_file!=NULL)
386 static void mp_enc_getline (MP mp) {
391 print_err("unexpected end of file");
394 p = mp->ps->enc_line;
396 c = enc_getchar (mp);
397 append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
399 append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
400 if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
403 static void mp_load_enc (MP mp, char *enc_name,
404 char **enc_encname, char **glyph_names){
405 char buf[ENC_BUF_SIZE], *p, *r;
408 unsigned save_selector = mp->selector;
409 if (!mp_enc_open (mp,enc_name)) {
410 mp_print (mp,"cannot open encoding file for reading");
413 mp_normalize_selector(mp);
415 mp_print (mp, enc_name);
417 if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
418 remove_eol (r, mp->ps->enc_line);
419 print_err ("invalid encoding vector (a name or `[' missing): `");
420 mp_print(mp,mp->ps->enc_line);
424 while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
425 myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
426 memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
427 *(myname+(r-mp->ps->enc_line-1))=0;
428 *enc_encname = myname;
436 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
439 if (names_count > 256) {
440 print_err ("encoding vector contains more than 256 names");
443 if (mp_xstrcmp (buf, notdef) != 0)
444 glyph_names[names_count] = mp_xstrdup (mp,buf);
447 if (*r != 10 && *r != '%') {
448 if (str_prefix (r, "] def"))
451 remove_eol (r, mp->ps->enc_line);
453 ("invalid encoding vector: a name or `] def' expected: `");
454 mp_print(mp,mp->ps->enc_line);
460 r = mp->ps->enc_line;
465 mp->selector = save_selector;
467 static void mp_read_enc (MP mp, enc_entry * e) {
471 mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
475 @ |write_enc| is used to write either external encoding (given in map file) or
476 internal encoding (read from the font file);
477 the 2nd argument is a pointer to the encoding entry;
480 static void mp_write_enc (MP mp, enc_entry * e) {
484 if (e->objnum != 0) /* the encoding has been written already */
489 mp_ps_print(mp,"\n%%%%BeginResource: encoding ");
490 mp_ps_print(mp, e->enc_name);
491 mp_ps_print_nl(mp, "/");
492 mp_ps_print(mp, e->enc_name);
493 mp_ps_print(mp, " [ ");
494 foffset = strlen(e->file_name)+3;
495 for (i = 0; i < 256; i++) {
497 if (s+1+foffset>=80) {
502 mp_ps_print_char(mp,'/');
503 mp_ps_print(mp, g[i]);
504 mp_ps_print_char(mp,' ');
508 mp_ps_print_nl (mp,"] def\n");
509 mp_ps_print(mp,"%%%%EndResource");
513 @ All encoding entries go into AVL tree for fast search by name.
516 struct avl_table *enc_tree;
518 @ Memory management functions for avl
520 @<Static variables in the outer block@>=
521 static const char notdef[] = ".notdef";
524 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
525 static void avl_xfree (struct libavl_allocator *allocator, void *block);
528 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
530 return malloc (size);
532 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
538 struct libavl_allocator avl_xallocator;
540 @ @<Set initial...@>=
541 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
542 mp->ps->avl_xallocator.libavl_free= avl_xfree;
543 mp->ps->enc_tree = NULL;
546 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
548 return strcmp (((const enc_entry *) pa)->file_name,
549 ((const enc_entry *) pb)->file_name);
551 static enc_entry * mp_add_enc (MP mp, char *s) {
555 if (mp->ps->enc_tree == NULL) {
556 mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
559 p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
560 if (p != NULL) /* encoding already registered */
562 p = mp_xmalloc (mp,1,sizeof (enc_entry));
564 p->file_name = mp_xstrdup (mp,s);
567 p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
568 for (i = 0; i < 256; i++)
569 p->glyph_names[i] = (char *) notdef;
570 aa = avl_probe (mp->ps->enc_tree, p);
577 static void mp_destroy_enc_entry (void *pa, void *pb) {
580 p = (enc_entry *) pa;
582 mp_xfree (p->file_name);
583 if (p->glyph_names != NULL)
584 for (i = 0; i < 256; i++)
585 if (p->glyph_names[i] != notdef)
586 mp_xfree (p->glyph_names[i]);
587 mp_xfree (p->glyph_names);
592 static void enc_free (MP mp);
594 @ @c static void enc_free (MP mp) {
595 if (mp->ps->enc_tree != NULL)
596 avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
600 static void mp_reload_encodings (MP mp) ;
601 static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) ;
603 @ @c void mp_reload_encodings (MP mp) {
607 font_number lastfnum = mp->last_fnum;
608 for (f=null_font+1;f<=lastfnum;f++) {
609 if (mp->font_enc_name[f]!=NULL ) {
610 mp_xfree(mp->font_enc_name[f]);
611 mp->font_enc_name[f]=NULL;
613 if (mp_has_fm_entry (mp,f,&fm_cur)) {
614 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
615 e = fm_cur->encoding;
621 static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) {
625 for (f=null_font+1;f<=lastfnum;f++) {
626 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
627 if (fm != NULL && (fm->ps_name != NULL)) {
628 if (is_reencoded (fm)) {
629 if (encodings_only || (!is_subsetted (fm))) {
631 mp_write_enc (mp, e);
632 /* clear for next run */
641 @* \[44b] Parsing font map files.
649 @d fm_close() (mp->close_file)(mp,mp->ps->fm_file)
650 @d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file)
653 static int fm_getchar(MP mp) {
655 unsigned char abyte=0;
656 void *byte_ptr = &abyte;
657 (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
665 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
666 enum _ltype { MAPFILE, MAPLINE };
667 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
668 typedef struct mitem {
669 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
670 int type; /* map file or map line */
671 char *map_line; /* pointer to map file name or map line */
672 int lineno; /* line number in map file */
678 fm_entry *loaded_tfm_found;
679 fm_entry *avail_tfm_found;
680 fm_entry *non_tfm_found;
681 fm_entry *not_avail_tfm_found;
683 @ @<Set initial...@>=
684 mp->ps->mitem = NULL;
687 static const char nontfm[] = "<nontfm>";
690 @d read_field(r, q, buf) do {
692 while (*r != ' ' && *r != '\0')
700 fm->F = mp_xstrdup(mp,buf);
712 static fm_entry *new_fm_entry (MP mp) {
714 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
719 fm->subset_tag = NULL;
721 fm->tfm_num = null_font;
722 fm->tfm_avail = TFM_UNCHECKED;
730 fm->all_glyphs = false;
737 static void delete_fm_entry (fm_entry * fm) {
738 mp_xfree (fm->tfm_name);
739 mp_xfree (fm->ps_name);
740 mp_xfree (fm->ff_name);
741 mp_xfree (fm->subset_tag);
742 mp_xfree (fm->charset);
746 static ff_entry *new_ff_entry (MP mp) {
748 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
754 static void delete_ff_entry (ff_entry * ff) {
755 mp_xfree (ff->ff_name);
756 mp_xfree (ff->ff_path);
760 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
761 static char buf[SMALL_BUF_SIZE];
762 char *p = tfmname, *r = strend (p) - 1, *q = r;
763 while (q > p && mp_isdigit (*q))
765 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
767 check_buf (q - p + 1, SMALL_BUF_SIZE);
768 strncpy (buf, p, (size_t) (q - p));
775 static boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
778 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
779 fm_entry *res = NULL;
780 res = mp_fm_lookup (mp, f);
784 return (res != NULL);
788 struct avl_table *tfm_tree;
789 struct avl_table *ps_tree;
790 struct avl_table *ff_tree;
792 @ @<Set initial...@>=
793 mp->ps->tfm_tree = NULL;
794 mp->ps->ps_tree = NULL;
795 mp->ps->ff_tree = NULL;
797 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
800 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
802 return strcmp (((const fm_entry *) pa)->tfm_name,
803 ((const fm_entry *) pb)->tfm_name);
806 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
808 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
810 const fm_entry *p1 = (const fm_entry *) pa;
811 const fm_entry *p2 = (const fm_entry *) pb;
813 assert (p1->ps_name != NULL && p2->ps_name != NULL);
814 if ((i = strcmp (p1->ps_name, p2->ps_name)))
816 cmp_return (p1->slant, p2->slant);
817 cmp_return (p1->extend, p2->extend);
818 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
819 (i = strcmp (p1->tfm_name, p2->tfm_name)))
824 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
826 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
828 return strcmp (((const ff_entry *) pa)->ff_name,
829 ((const ff_entry *) pb)->ff_name);
832 @ @c static void create_avl_trees (MP mp) {
833 if (mp->ps->tfm_tree == NULL) {
834 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
835 assert (mp->ps->tfm_tree != NULL);
837 if (mp->ps->ps_tree == NULL) {
838 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
839 assert (mp->ps->ps_tree != NULL);
841 if (mp->ps->ff_tree == NULL) {
842 mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
843 assert (mp->ps->ff_tree != NULL);
847 @ The function |avl_do_entry| is not completely symmetrical with regards
848 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
849 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
850 with the original version.
854 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
855 @d set_pslink(fm) ((fm)->links |= LINK_PS)
856 @d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
857 @d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
858 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
859 @d has_pslink(fm) ((fm)->links & LINK_PS)
862 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
868 /* handle |tfm_name| link */
870 if (strcmp (fp->tfm_name, nontfm)) {
871 p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
873 if (mode == FM_DUPIGNORE) {
874 mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
878 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
879 if (mp_has_font_size(mp,p->tfm_num)) {
881 "fontmap entry for `%s' has been used, replace/delete not allowed",
886 a = avl_delete (mp->ps->tfm_tree, p);
893 if (mode != FM_DELETE) {
894 aa = avl_probe (mp->ps->tfm_tree, fp);
900 /* handle |ps_name| link */
902 if (fp->ps_name != NULL) {
903 assert (fp->tfm_name != NULL);
904 p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
906 if (mode == FM_DUPIGNORE) {
908 "ps_name entry for `%s' already exists, duplicates ignored",
912 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
913 if (mp_has_font_size(mp,p->tfm_num)) {
914 /* REPLACE/DELETE not allowed */
916 "fontmap entry for `%s' has been used, replace/delete not allowed",
921 a = avl_delete (mp->ps->ps_tree, p);
924 if (!has_tfmlink (p))
928 if (mode != FM_DELETE) {
929 aa = avl_probe (mp->ps->ps_tree, fp);
935 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
936 return 1; /* deallocation of |fm_entry| structure required */
941 @ consistency check for map entry, with warn flag
944 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
948 if (fm->ps_name != NULL) {
949 if (is_basefont (fm)) {
950 if (is_fontfile (fm) && !is_included (fm)) {
952 mp_snprintf(s,128, "invalid entry for `%s': "
953 "font file must be included or omitted for base fonts",
959 } else { /* not a base font */
960 /* if no font file given, drop this entry */
961 /* |if (!is_fontfile (fm)) {
964 "invalid entry for `%s': font file missing",
973 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
976 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
982 if ((fm->slant != 0 || fm->extend != 0) &&
983 (is_truetype (fm))) {
986 "invalid entry for `%s': "
987 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
993 if (abs (fm->slant) > 1000) {
996 "invalid entry for `%s': too big value of SlantFont (%d/1000.0)",
997 fm->tfm_name, (int)fm->slant);
1002 if (abs (fm->extend) > 2000) {
1005 "invalid entry for `%s': too big value of ExtendFont (%d/1000.0)",
1006 fm->tfm_name, (int)fm->extend);
1011 if (fm->pid != -1 &&
1012 !(is_truetype (fm) && is_included (fm) &&
1013 is_subsetted (fm) && !is_reencoded (fm))) {
1016 "invalid entry for `%s': "
1017 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
1026 @ returns true if s is one of the 14 std. font names; speed-trimmed.
1028 @c static boolean check_basefont (char *s) {
1029 static const char *basefont_names[] = {
1030 "Courier", /* 0:7 */
1031 "Courier-Bold", /* 1:12 */
1032 "Courier-Oblique", /* 2:15 */
1033 "Courier-BoldOblique", /* 3:19 */
1034 "Helvetica", /* 4:9 */
1035 "Helvetica-Bold", /* 5:14 */
1036 "Helvetica-Oblique", /* 6:17 */
1037 "Helvetica-BoldOblique", /* 7:21 */
1039 "Times-Roman", /* 9:11 */
1040 "Times-Bold", /* 10:10 */
1041 "Times-Italic", /* 11:12 */
1042 "Times-BoldItalic", /* 12:16 */
1043 "ZapfDingbats" /* 13:12 */
1045 static const int Index[] =
1046 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1049 const size_t n = strlen (s);
1053 if (n == 12) { /* three names have length 12 */
1056 k = 1; /* Courier-Bold */
1059 k = 11; /* Times-Italic */
1062 k = 13; /* ZapfDingbats */
1069 if (k > -1 && !strcmp (basefont_names[k], s))
1075 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1077 @c static void fm_scan_line (MP mp) {
1078 int a, b, c, j, u = 0, v = 0;
1081 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1082 char *p, *q, *r, *s;
1084 switch (mp->ps->mitem->type) {
1088 c = fm_getchar (mp);
1089 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1095 r = mp->ps->mitem->map_line;
1100 if (*r == '\0' || is_cfg_comment (*r))
1102 fm = new_fm_entry (mp);
1103 read_field (r, q, buf);
1104 set_field (tfm_name);
1106 read_field (r, q, buf);
1107 if (*buf != '<' && *buf != '"')
1108 set_field (ps_name);
1110 r = p; /* unget the field */
1111 if (mp_isdigit (*r)) { /* font flags given */
1112 fm->flags = atoi (r);
1113 while (mp_isdigit (*r))
1116 while (1) { /* loop through "specials", encoding, font file */
1121 case '"': /* opening quote */
1126 if (sscanf (r, "%f %n", &d, &j) > 0) {
1127 s = r + j; /* jump behind number, eat also blanks, if any */
1128 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1129 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1130 if (str_prefix (s, "SlantFont")) {
1131 d *= 1000.0; /* correct rounding also for neg. numbers */
1132 fm->slant = (short int) (d > 0 ? d + 0.5 : d - 0.5);
1133 r = s + strlen ("SlantFont");
1134 } else if (str_prefix (s, "ExtendFont")) {
1136 fm->extend = (short int) (d > 0 ? d + 0.5 : d - 0.5);
1137 if (fm->extend == 1000)
1139 r = s + strlen ("ExtendFont");
1140 } else { /* unknown name */
1142 *r != ' ' && *r != '"' && *r != '\0';
1143 r++); /* jump over name */
1144 c = *r; /* remember char for temporary end of string */
1146 mp_snprintf(warn_s,128,
1147 "invalid entry for `%s': unknown name `%s' ignored",
1153 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1156 if (*r == '"') /* closing quote */
1159 mp_snprintf(warn_s,128,
1160 "invalid entry for `%s': closing quote missing",
1166 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1167 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1168 fm->pid = (short int)a;
1169 fm->eid = (short int)b;
1173 default: /* encoding or font file specification */
1177 if (*r == '<' || *r == '[')
1180 read_field (r, q, buf);
1181 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1182 if (strlen (buf) > 4 && mp_strcasecmp (strend (buf) - 4, ".enc") == 0) {
1183 fm->encoding = mp_add_enc (mp, buf);
1184 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1185 } else if (strlen (buf) > 0) { /* file name given */
1186 /* font file, formats:
1187 * subsetting: '<cmr10.pfa'
1188 * no subsetting: '<<cmr10.pfa'
1189 * no embedding: 'cmr10.pfa'
1191 if (a == '<' || u == '<') {
1193 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1195 /* otherwise b == '<' (or '[') => no subsetting */
1197 set_field (ff_name);
1206 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1208 if (is_fontfile (fm)
1209 && mp_strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1211 if (check_fm_entry (mp,fm, true) != 0)
1214 Until here the map line has been completely scanned without errors;
1215 fm points to a valid, freshly filled-out |fm_entry| structure.
1216 Now follows the actual work of registering/deleting.
1218 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
1221 delete_fm_entry (fm);
1225 @c static void fm_read_info (MP mp) {
1228 if (mp->ps->tfm_tree == NULL)
1229 create_avl_trees (mp);
1230 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1232 mp->ps->mitem->lineno = 1;
1233 switch (mp->ps->mitem->type) {
1235 n = mp->ps->mitem->map_line;
1236 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1237 if (!mp->ps->fm_file) {
1238 mp_snprintf(s,256,"cannot open font map file %s",n);
1241 unsigned save_selector = mp->selector;
1242 mp_normalize_selector(mp);
1245 while (!fm_eof ()) {
1247 mp->ps->mitem->lineno++;
1251 mp->selector = save_selector;
1252 mp->ps->fm_file = NULL;
1262 mp->ps->mitem->map_line = NULL; /* done with this line */
1266 @ @c static void init_fm (fm_entry * fm, font_number f) {
1267 if (fm->tfm_num == null_font ) {
1269 fm->tfm_avail = TFM_FOUND;
1274 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1277 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1282 if (mp->ps->tfm_tree == NULL)
1283 fm_read_info (mp); /* only to read default map file */
1284 tfm = mp->font_name[f];
1285 assert (strcmp (tfm, nontfm));
1286 /* Look up for full <tfmname>[+-]<expand> */
1288 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1291 return (fm_entry *) fm;
1293 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1294 if (tfm == NULL) /* not an expanded font, nothing to do */
1298 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1299 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1300 return (fm_entry *) fm; /* font expansion uses the base font */
1305 @ Early check whether a font file exists. Used e. g. for replacing fonts
1306 of embedded PDF files: Without font file, the font within the embedded
1307 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1308 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1309 only once per font file name + expansion parameter. This might help
1310 keeping speed, if many PDF pages with same fonts are to be embedded.
1312 The |ff_tree| contains only font files, which are actually needed,
1313 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1316 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1321 assert (fm->ff_name != NULL);
1322 tmp.ff_name = fm->ff_name;
1323 ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1324 if (ff == NULL) { /* not yet in database */
1325 ff = new_ff_entry (mp);
1326 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1327 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1328 aa = avl_probe (mp->ps->ff_tree, ff);
1329 assert (aa != NULL);
1334 @ Process map file given by its name or map line contents. Items not
1335 beginning with [+-=] flush default map file, if it has not yet been
1336 read. Leading blanks and blanks immediately following [+-=] are ignored.
1339 @c static void mp_process_map_item (MP mp, char *s, int type) {
1343 s++; /* ignore leading blank */
1345 case '+': /* +mapfile.map, +mapline */
1346 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1349 case '=': /* =mapfile.map, =mapline */
1350 mode = FM_REPLACE; /* try to replace earlier entry */
1353 case '-': /* -mapfile.map, -mapline */
1354 mode = FM_DELETE; /* try to delete entry */
1358 mode = FM_DUPIGNORE; /* like +, but also: */
1359 mp_xfree(mp->ps->mitem->map_line);
1360 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1363 s++; /* ignore blank after [+-=] */
1364 p = s; /* map item starts here */
1366 case MAPFILE: /* remove blank at end */
1367 while (*p != '\0' && *p != ' ')
1371 case MAPLINE: /* blank at end allowed */
1376 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1378 if (*s != '\0') { /* only if real item to process */
1379 mp->ps->mitem->mode = mode;
1380 mp->ps->mitem->type = type;
1381 mp->ps->mitem->map_line = s;
1386 @ @<Exported function headers@>=
1387 void mp_map_file (MP mp, str_number t);
1388 void mp_map_line (MP mp, str_number t);
1389 void mp_init_map_file (MP mp, int is_troff);
1392 void mp_map_file (MP mp, str_number t) {
1393 char *s = mp_xstrdup(mp,mp_str (mp,t));
1394 mp_process_map_item (mp, s, MAPFILE);
1397 void mp_map_line (MP mp, str_number t) {
1398 char *s = mp_xstrdup(mp,mp_str (mp,t));
1399 mp_process_map_item (mp, s, MAPLINE);
1404 @c void mp_init_map_file (MP mp, int is_troff) {
1406 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1407 mp->ps->mitem->mode = FM_DUPIGNORE;
1408 mp->ps->mitem->type = MAPFILE;
1409 mp->ps->mitem->map_line = NULL;
1410 r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap);
1413 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1416 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1418 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1423 @ @<Dealloc variables@>=
1424 if (mp->ps->mitem!=NULL) {
1425 mp_xfree(mp->ps->mitem->map_line);
1426 mp_xfree(mp->ps->mitem);
1432 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1435 fm = (fm_entry *) pa;
1436 if (!has_pslink (fm))
1437 delete_fm_entry (fm);
1441 static void destroy_fm_entry_ps (void *pa, void *pb) {
1444 fm = (fm_entry *) pa;
1445 if (!has_tfmlink (fm))
1446 delete_fm_entry (fm);
1450 static void destroy_ff_entry (void *pa, void *pb) {
1453 ff = (ff_entry *) pa;
1454 delete_ff_entry (ff);
1458 static void fm_free (MP mp);
1461 static void fm_free (MP mp) {
1462 if (mp->ps->tfm_tree != NULL)
1463 avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1464 if (mp->ps->ps_tree != NULL)
1465 avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1466 if (mp->ps->ff_tree != NULL)
1467 avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1470 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1471 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1472 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
1473 one or more spaces, a PostScript name, and possibly a space and some other junk.
1474 This routine reads the table, updates |font_ps_name| entries starting after
1475 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
1477 @d ps_tab_name "psfonts.map" /* locates font name translation table */
1480 static void mp_read_psname_table (MP mp) ;
1483 static void mp_read_psname_table (MP mp) {
1485 if (mp->ps->mitem == NULL) {
1486 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1487 mp->ps->mitem->mode = FM_DUPIGNORE;
1488 mp->ps->mitem->type = MAPFILE;
1489 mp->ps->mitem->map_line = NULL;
1491 mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name);
1493 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1494 if (mp_has_fm_entry(mp, k, NULL)) {
1495 mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1498 mp->last_ps_fnum=mp->last_fnum;
1502 @ The traditional function is a lot shorter now.
1506 @* \[44c] Helper functions for Type1 fonts.
1509 typedef char char_entry;
1510 typedef unsigned char Byte;
1514 char_entry *char_ptr, *char_array;
1516 char *job_id_string;
1518 @ @<Set initial...@>=
1519 mp->ps->char_array = NULL;
1520 mp->ps->job_id_string = NULL;
1523 @d SMALL_ARRAY_SIZE 256
1527 void mp_set_job_id (MP mp) {
1528 char *name_string, *format_string, *s;
1530 if (mp->ps->job_id_string != NULL)
1532 if ( mp->job_name==NULL )
1533 mp->job_name = mp_xstrdup(mp,"mpout");
1534 name_string = mp_xstrdup (mp,mp->job_name);
1535 format_string = mp_xstrdup (mp,mp->mem_ident);
1536 slen = SMALL_BUF_SIZE +
1537 strlen (name_string) +
1538 strlen (format_string);
1539 s = mp_xmalloc (mp,slen, sizeof (char));
1540 @= /*@@-bufferoverflowhigh@@*/ @>
1541 sprintf (s,"%.4u/%.2u/%.2u %.2u:%.2u %s %s",
1542 ((unsigned)mp->internal[mp_year]>>16),
1543 ((unsigned)mp->internal[mp_month]>>16),
1544 ((unsigned)mp->internal[mp_day]>>16),
1545 ((unsigned)mp->internal[mp_time]>>16) / 60,
1546 ((unsigned)mp->internal[mp_time]>>16) % 60,
1547 name_string, format_string);
1548 @= /*@@=bufferoverflowhigh@@*/ @>
1549 mp->ps->job_id_string = mp_xstrdup (mp,s);
1551 mp_xfree (name_string);
1552 mp_xfree (format_string);
1554 static void fnstr_append (MP mp, const char *s) {
1555 size_t l = strlen (s) + 1;
1556 alloc_array (char, l, SMALL_ARRAY_SIZE);
1557 strcat (mp->ps->char_ptr, s);
1558 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1561 @ @<Exported function headers@>=
1562 void mp_set_job_id (MP mp) ;
1564 @ @<Dealloc variables@>=
1565 mp_xfree(mp->ps->job_id_string);
1567 @ this is not really a true crc32, but it should be just enough to keep
1568 subsets prefixes somewhat disjunct
1571 static unsigned long crc32 (unsigned long oldcrc, const Byte *buf, size_t len) {
1572 unsigned long ret = 0;
1575 ret = (unsigned long)((23<<24)+(45<<16)+(67<<8)+89);
1578 ret = (ret<<2)+buf[i];
1581 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1582 integer b; /* |char_base[f]| */
1584 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1590 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, font_number tex_font)
1596 if (mp->ps->job_id_string ==NULL)
1597 mp_fatal_error(mp, "no job id!");
1598 l = strlen (mp->ps->job_id_string) + 1;
1600 alloc_array (char, l, SMALL_ARRAY_SIZE);
1601 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1602 mp->ps->char_ptr = strend (mp->ps->char_array);
1603 if (fm_cur->tfm_name != NULL) {
1604 fnstr_append (mp," TFM name: ");
1605 fnstr_append (mp,fm_cur->tfm_name);
1607 fnstr_append (mp," PS name: ");
1608 if (fm_cur->ps_name != NULL)
1609 fnstr_append (mp,fm_cur->ps_name);
1610 fnstr_append (mp," Encoding: ");
1611 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1612 fnstr_append (mp,(fm_cur->encoding)->file_name);
1614 fnstr_append (mp,"built-in");
1615 fnstr_append (mp," CharSet: ");
1616 for (i = 0; i < 256; i++)
1617 if (mp_char_marked (mp,tex_font, (eight_bits)i) && glyph_names[i] != notdef) {
1618 if (glyph_names[i]!=NULL) {
1619 fnstr_append (mp,"/");
1620 fnstr_append (mp,glyph_names[i]);
1623 if (fm_cur->charset != NULL) {
1624 fnstr_append (mp," Extra CharSet: ");
1625 fnstr_append (mp, fm_cur->charset);
1627 crc = crc32 (0L, Z_NULL, 0);
1628 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1629 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1630 * there are 26 uppercase chars ==> each char represents a number in range
1631 * |0..25|. The maximal number that can be represented by the tag is
1632 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1633 * of the CRC must be dropped out.
1635 for (i = 0; i < 6; i++) {
1636 tag[i] = 'A' + crc % 26;
1640 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1646 @d external_enc() (fm_cur->encoding)->glyph_names
1647 @d is_used_char(c) mp_char_marked (mp, tex_font, (eight_bits)c)
1648 @d end_last_eexec_line()
1649 mp->ps->hexline_length = HEXLINE_WIDTH;
1651 mp->ps->t1_eexec_encrypt = false
1652 @d t1_log(s) mp_print(mp,(char *)s)
1653 @d t1_putchar(c) wps_chr(c)
1654 @d embed_all_glyphs(tex_font) false
1656 @d extra_charset() mp->ps->dvips_extra_charset
1657 @d update_subset_tag()
1658 @d fixedcontent true
1661 #define PRINTF_BUF_SIZE 1024
1662 char *dvips_extra_charset;
1664 unsigned char *grid;
1665 char *ext_glyph_names[256];
1666 char print_buf[PRINTF_BUF_SIZE];
1667 int t1_byte_waiting;
1669 @ @<Set initial ...@>=
1670 mp->ps->dvips_extra_charset=NULL;
1671 mp->ps->t1_byte_waiting=0;
1674 @d t1_ungetchar(A) mp->ps->t1_byte_waiting=A
1675 @d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file)
1676 @d t1_close() (mp->close_file)(mp,mp->ps->t1_file)
1677 @d valid_code(c) (c >= 0 && c < 256)
1680 static int t1_getchar (MP mp) {
1683 void *byte_ptr = &abyte;
1684 if (mp->ps->t1_byte_waiting) {
1685 abyte = mp->ps->t1_byte_waiting;
1686 mp->ps->t1_byte_waiting = 0;
1688 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
1693 @ @<Static variables in the outer block@>=
1694 static const char *standard_glyph_names[256] =
1695 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1696 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1697 notdef, notdef, notdef, notdef, notdef, notdef,
1698 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1699 "space", "exclam", "quotedbl", "numbersign",
1700 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1701 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1702 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1703 "eight", "nine", "colon", "semicolon", "less",
1704 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1705 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1706 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1707 "backslash", "bracketright", "asciicircum", "underscore",
1708 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1709 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1710 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1711 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1712 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1713 notdef, notdef, notdef, notdef, notdef, notdef,
1714 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1715 notdef, notdef, notdef, "exclamdown", "cent",
1716 "sterling", "fraction", "yen", "florin", "section", "currency",
1717 "quotesingle", "quotedblleft", "guillemotleft",
1718 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1719 "dagger", "daggerdbl", "periodcentered", notdef,
1720 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1721 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1722 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1723 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1724 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1725 notdef, notdef, notdef, notdef, notdef, notdef,
1726 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1727 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1728 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1729 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1730 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1731 "germandbls", notdef, notdef, notdef, notdef };
1732 static const char charstringname[] = "/CharStrings";
1735 char **t1_glyph_names;
1736 char *t1_builtin_glyph_names[256];
1737 char charsetstr[0x4000];
1738 boolean read_encoding_only;
1742 #define T1_BUF_SIZE 0x10
1746 #define CS_VMOVETO 4
1747 #define CS_RLINETO 5
1748 #define CS_HLINETO 6
1749 #define CS_VLINETO 7
1750 #define CS_RRCURVETO 8
1751 #define CS_CLOSEPATH 9
1752 #define CS_CALLSUBR 10
1753 #define CS_RETURN 11
1754 #define CS_ESCAPE 12
1756 #define CS_ENDCHAR 14
1757 #define CS_RMOVETO 21
1758 #define CS_HMOVETO 22
1759 #define CS_VHCURVETO 30
1760 #define CS_HVCURVETO 31
1761 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1763 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1764 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1765 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1766 #define CS_SEAC CS_1BYTE_MAX + 6
1767 #define CS_SBW CS_1BYTE_MAX + 7
1768 #define CS_DIV CS_1BYTE_MAX + 12
1769 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1770 #define CS_POP CS_1BYTE_MAX + 17
1771 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1772 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1773 #define CS_MAX CS_2BYTE_MAX
1776 typedef unsigned char byte;
1778 byte nargs; /* number of arguments */
1779 boolean bottom; /* take arguments from bottom of stack? */
1780 boolean clear; /* clear stack? */
1782 } cc_entry; /* CharString Command */
1784 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1786 unsigned short len; /* length of the whole string */
1787 unsigned short cslen; /* length of the encoded part of the string */
1797 unsigned short t1_dr, t1_er;
1798 unsigned short t1_cslen;
1802 typedef char t1_line_entry;
1803 typedef char t1_buf_entry;
1806 t1_line_entry *t1_line_ptr, *t1_line_array;
1807 size_t t1_line_limit;
1808 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1809 size_t t1_buf_limit;
1811 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1812 char *cs_dict_start, *cs_dict_end;
1813 int cs_count, cs_size, cs_size_pos;
1815 char *subr_array_start, *subr_array_end;
1816 int subr_max, subr_size, subr_size_pos;
1818 @ @<Set initial...@>=
1819 mp->ps->t1_line_array = NULL;
1820 mp->ps->t1_buf_array = NULL;
1823 This list contains the begin/end tokens commonly used in the
1824 /Subrs array of a Type 1 font.
1826 @<Static variables in the outer block@>=
1827 static const char *cs_token_pairs_list[][2] = {
1830 {" RD", "noaccess put"},
1831 {" -|", "noaccess put"},
1836 const char **cs_token_pair;
1837 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1838 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1839 int t1_block_length;
1847 @<Set initial ...@>=
1848 mp->ps->hexline_length = 0;
1851 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1852 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1853 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1854 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1855 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1856 @d t1_subrs() t1_prefix("/Subrs")
1857 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1858 @d t1_cleartomark() t1_prefix("cleartomark")
1861 static void end_hexline (MP mp) {
1862 if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
1864 mp->ps->hexline_length = 0;
1867 static void t1_check_pfa (MP mp) {
1868 const int c = t1_getchar (mp);
1869 mp->ps->t1_pfa = (c != 128) ? true : false;
1872 static int t1_getbyte (MP mp)
1874 int c = t1_getchar (mp);
1877 if (mp->ps->t1_block_length == 0) {
1879 mp_fatal_error (mp, "invalid marker");
1880 c = t1_getchar (mp);
1883 (void)t1_getchar (mp);
1886 mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
1887 mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 8;
1888 mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 16;
1889 mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 24;
1890 c = t1_getchar (mp);
1892 mp->ps->t1_block_length--;
1895 static int hexval (int c) {
1896 if (c >= 'A' && c <= 'F')
1897 return c - 'A' + 10;
1898 else if (c >= 'a' && c <= 'f')
1899 return c - 'a' + 10;
1900 else if (c >= '0' && c <= '9')
1905 static byte edecrypt (MP mp, byte cipher) {
1907 if (mp->ps->t1_pfa) {
1908 while (cipher == 10 || cipher == 13)
1909 cipher = (byte)t1_getbyte (mp);
1910 mp->ps->last_hexbyte = cipher = (byte)(((byte)hexval (cipher) << 4) +
1911 hexval (t1_getbyte (mp)));
1913 plain = (cipher ^ (mp->ps->t1_dr >> 8));
1914 mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * t1_c1 + t1_c2;
1917 static byte cdecrypt (byte cipher, unsigned short *cr)
1919 const byte plain = (cipher ^ (*cr >> 8));
1920 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1923 static byte eencrypt (MP mp, byte plain)
1925 const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1926 mp->ps->t1_er = (cipher + mp->ps->t1_er) * t1_c1 + t1_c2;
1930 static byte cencrypt (byte plain, unsigned short *cr)
1932 const byte cipher = (plain ^ (*cr >> 8));
1933 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1937 static char *eol (char *s) {
1938 char *p = strend (s);
1939 if (p!=NULL && p - s > 1 && p[-1] != 10) {
1945 static float t1_scan_num (MP mp, char *p, char **r)
1950 if (sscanf (p, "%g", &f) != 1) {
1951 remove_eol (p, mp->ps->t1_line_array);
1952 mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1953 mp_fatal_error(mp,s);
1956 for (; mp_isdigit (*p) || *p == '.' ||
1957 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1963 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1966 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1969 while (s1 >= begin_buf && s2 >= s) {
1978 @d alloc_array(T, n, s) do {
1979 if (mp->ps->T##_array == NULL) {
1980 mp->ps->T##_limit = (s);
1981 if ((size_t)(n) > mp->ps->T##_limit)
1982 mp->ps->T##_limit = (size_t)(n);
1983 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
1984 mp->ps->T##_ptr = mp->ps->T##_array;
1986 else if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
1987 size_t last_ptr_index;
1988 last_ptr_index = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array);
1989 mp->ps->T##_limit *= 2;
1990 if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
1991 mp->ps->T##_limit = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n));
1992 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
1993 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
1997 @d out_eexec_char(A) t1_outhex(mp,(A))
2000 static void t1_outhex (MP mp, byte b)
2002 static const char *hexdigits = "0123456789ABCDEF";
2003 t1_putchar (hexdigits[b / 16]);
2004 t1_putchar (hexdigits[b % 16]);
2005 mp->ps->hexline_length += 2;
2008 static void t1_getline (MP mp) {
2009 int c, l, eexec_scan;
2011 static const char eexec_str[] = "currentfile eexec";
2012 static int eexec_len = 17; /* |strlen(eexec_str)| */
2015 mp_fatal_error (mp,"unexpected end of file");
2016 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2017 alloc_array (t1_line, 1, T1_BUF_SIZE);
2018 mp->ps->t1_cslen = 0;
2020 c = t1_getbyte (mp);
2023 while (!t1_eof ()) {
2024 if (mp->ps->t1_in_eexec == 1)
2025 c = edecrypt (mp,(byte)c);
2026 alloc_array (t1_line, 1, T1_BUF_SIZE);
2027 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2028 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
2029 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
2034 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2036 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
2037 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2038 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2039 p = mp->ps->t1_line_ptr - 5;
2042 l = (int)t1_scan_num (mp, p + 1, 0);
2043 mp->ps->t1_cslen = (unsigned short)l;
2044 mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2045 /* |mp->ps->cs_start| is an index now */
2046 alloc_array (t1_line, l, T1_BUF_SIZE);
2048 *mp->ps->t1_line_ptr = (t1_line_entry)edecrypt (mp,(byte)t1_getbyte (mp));
2049 mp->ps->t1_line_ptr++;
2052 c = t1_getbyte (mp);
2054 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
2055 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2056 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2058 if (eexec_scan == eexec_len)
2059 mp->ps->t1_in_eexec = 1;
2061 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2062 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2063 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2066 static void t1_putline (MP mp)
2068 char *p = mp->ps->t1_line_array;
2069 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2071 if (mp->ps->t1_eexec_encrypt) {
2072 while (p < mp->ps->t1_line_ptr)
2073 out_eexec_char (eencrypt (mp,(byte)*p++));
2075 while (p < mp->ps->t1_line_ptr)
2080 static void t1_puts (MP mp, const char *s)
2082 if (s != mp->ps->t1_line_array)
2083 strcpy (mp->ps->t1_line_array, s);
2084 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2088 static void t1_init_params (MP mp, const char *open_name_prefix,
2089 char *cur_file_name) {
2090 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2091 t1_log (open_name_prefix);
2092 t1_log (cur_file_name);
2094 mp->ps->t1_lenIV = 4;
2095 mp->ps->t1_dr = 55665;
2096 mp->ps->t1_er = 55665;
2097 mp->ps->t1_in_eexec = 0;
2098 mp->ps->t1_cs = false;
2099 mp->ps->t1_scan = true;
2100 mp->ps->t1_synthetic = false;
2101 mp->ps->t1_eexec_encrypt = false;
2102 mp->ps->t1_block_length = 0;
2105 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2106 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2107 t1_log (close_name_suffix);
2112 static void t1_check_block_len (MP mp, boolean decrypt) {
2115 if (mp->ps->t1_block_length == 0)
2117 c = t1_getbyte (mp);
2119 c = edecrypt (mp,(byte)c);
2120 l = mp->ps->t1_block_length;
2121 if (!(l == 0 && (c == 10 || c == 13))) {
2122 mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2125 (void)t1_getbyte (mp);
2128 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2130 if (!mp->ps->t1_pfa)
2131 t1_check_block_len (mp, false);
2132 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2133 (void)edecrypt (mp, (byte)t1_getbyte (mp));
2134 *mp->ps->t1_line_ptr++ = 0;
2136 mp->ps->t1_eexec_encrypt = true;
2137 if (!mp->ps->read_encoding_only)
2138 if (is_included (fm_cur))
2139 t1_putline (mp); /* to put the first four bytes */
2141 static void t1_stop_eexec (MP mp) {
2143 end_last_eexec_line ();
2144 if (!mp->ps->t1_pfa)
2145 t1_check_block_len (mp,true);
2147 c = edecrypt (mp, (byte)t1_getbyte (mp));
2148 if (!(c == 10 || c == 13)) {
2149 if (mp->ps->last_hexbyte == 0)
2152 mp_warn (mp,"unexpected data after eexec");
2155 mp->ps->t1_cs = false;
2156 mp->ps->t1_in_eexec = 2;
2158 static void t1_modify_fm (MP mp) {
2159 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2162 static void t1_modify_italic (MP mp) {
2163 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2168 const char *pdfname;
2178 static key_entry font_keys[FONT_KEYS_NUM] = {
2179 {"Ascent", "Ascender", 0, false},
2180 {"CapHeight", "CapHeight", 0, false},
2181 {"Descent", "Descender", 0, false},
2182 {"FontName", "FontName", 0, false},
2183 {"ItalicAngle", "ItalicAngle", 0, false},
2184 {"StemV", "StdVW", 0, false},
2185 {"XHeight", "XHeight", 0, false},
2186 {"FontBBox", "FontBBox", 0, false},
2198 @d ITALIC_ANGLE_CODE 4
2204 @d FONTBBOX4_CODE 10
2205 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2208 static void t1_scan_keys (MP mp, font_number tex_font,fm_entry *fm_cur) {
2212 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2213 if (t1_prefix ("/FontMatrix")) {
2217 if (t1_prefix ("/ItalicAngle")) {
2218 t1_modify_italic (mp);
2222 if (t1_prefix ("/FontType")) {
2223 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2224 if ((i = (int)t1_scan_num (mp,p, 0)) != 1) {
2226 mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2227 mp_fatal_error(mp,s);
2231 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2232 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2234 if (key - font_keys == MAX_KEY_CODE)
2237 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2239 if ((k = key - font_keys) == FONTNAME_CODE) {
2242 remove_eol (p, mp->ps->t1_line_array);
2243 mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2244 mp_fatal_error(mp,s);
2246 r = ++p; /* skip the slash */
2247 if (is_included (fm_cur)) {
2248 /* save the fontname */
2249 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2250 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2251 mp->ps->fontname_buf[i]=0;
2253 if(is_subsetted (fm_cur)) {
2254 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2255 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2257 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2259 alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
2261 strncpy (r, fm_cur->subset_tag , 6);
2263 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2264 mp->ps->t1_line_ptr = eol (r);
2266 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2268 mp->ps->t1_line_ptr = eol (r);
2273 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2274 && (*p == '[' || *p == '{'))
2276 if (k == FONTBBOX1_CODE) {
2277 for (i = 0; i < 4; i++) {
2278 key[i].value = t1_scan_num (mp, p, &r);
2283 key->value = t1_scan_num (mp, p, 0);
2285 static void t1_scan_param (MP mp, font_number tex_font,fm_entry *fm_cur)
2287 static const char *lenIV = "/lenIV";
2288 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2290 if (t1_prefix (lenIV)) {
2291 mp->ps->t1_lenIV = (short int)t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2294 t1_scan_keys (mp, tex_font,fm_cur);
2296 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2297 if (glyph_names[b] != notdef) {
2298 mp_xfree (glyph_names[b]);
2299 glyph_names[b] = (char *) notdef;
2301 if (glyph_names[a] != notdef) {
2302 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2305 static void t1_builtin_enc (MP mp) {
2306 int i, a, b, c, counter = 0;
2309 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2311 if (t1_suffix ("def")) { /* predefined encoding */
2312 (void)sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2313 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2314 for (i = 0; i < 256; i++)
2315 if (standard_glyph_names[i] == notdef)
2316 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2318 mp->ps->t1_builtin_glyph_names[i] =
2319 mp_xstrdup (mp,standard_glyph_names[i]);
2320 mp->ps->t1_encoding = ENC_STANDARD;
2323 mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2324 mp->ps->t1_buf_array);
2325 mp_fatal_error(mp,s);
2329 mp->ps->t1_encoding = ENC_BUILTIN;
2331 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2332 * not a predefined encoding
2334 * We have two possible forms of Encoding vector. The first case is
2336 * /Encoding [/a /b /c...] readonly def
2338 * and the second case can look like
2340 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2346 for (i = 0; i < 256; i++)
2347 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2348 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2349 r = strchr (mp->ps->t1_line_array, '[') + 1;
2353 for (p = mp->ps->t1_buf_array, r++;
2354 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2358 if (counter > 255) {
2360 (mp, "encoding vector contains more than 256 names");
2362 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2363 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2366 if (*r != 10 && *r != '%') {
2367 if (str_prefix (r, "] def")
2368 || str_prefix (r, "] readonly def"))
2372 remove_eol (r, mp->ps->t1_line_array);
2373 mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2374 mp->ps->t1_line_array);
2375 mp_fatal_error(mp,s);
2379 r = mp->ps->t1_line_array;
2381 } else { /* the second case */
2382 p = strchr (mp->ps->t1_line_array, 10);
2386 p = mp->ps->t1_line_array;
2389 check for `dup <index> <glyph> put'
2391 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2392 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2393 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2394 mp->ps->t1_builtin_glyph_names[i] =
2395 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2396 p = strstr (p, " put") + strlen (" put");
2400 check for `dup dup <to> exch <from> get put'
2402 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2403 && valid_code (a) && valid_code (b)) {
2404 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2405 p = strstr (p, " get put") + strlen (" get put");
2409 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2412 (p, "dup dup %i %i getinterval %i exch putinterval",
2413 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2414 && valid_code (c)) {
2415 for (i = 0; i < c; i++)
2416 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2417 p = strstr (p, " putinterval") + strlen (" putinterval");
2421 check for `def' or `readonly def'
2423 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2424 && strcmp (p, "def\n") == 0)
2427 skip an unrecognizable word
2430 while (*p != ' ' && *p != 10)
2438 static void t1_check_end (MP mp) {
2442 if (t1_prefix ("{restore}"))
2448 char *ff_name; /* base name of font file */
2449 char *ff_path; /* full path to font file */
2453 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2455 ff = check_ff_exist (mp, fm_cur);
2456 if (ff->ff_path != NULL) {
2457 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font);
2459 mp_warn (mp, "cannot open Type 1 font file for reading");
2462 t1_init_params (mp,open_name_prefix,fm_cur->ff_name);
2463 mp->ps->fontfile_found = true;
2467 static void t1_scan_only (MP mp, font_number tex_font, fm_entry *fm_cur) {
2470 t1_scan_param (mp,tex_font, fm_cur);
2472 while (mp->ps->t1_in_eexec == 0);
2473 t1_start_eexec (mp,fm_cur);
2476 t1_scan_param (mp,tex_font, fm_cur);
2478 while (!(t1_charstrings () || t1_subrs ()));
2481 static void t1_include (MP mp, font_number tex_font, fm_entry *fm_cur) {
2484 t1_scan_param (mp,tex_font, fm_cur);
2487 while (mp->ps->t1_in_eexec == 0);
2488 t1_start_eexec (mp,fm_cur);
2491 t1_scan_param (mp,tex_font, fm_cur);
2494 while (!(t1_charstrings () || t1_subrs ()));
2495 mp->ps->t1_cs = true;
2500 while (!t1_end_eexec ());
2502 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2507 while (!t1_cleartomark ());
2508 t1_check_end (mp); /* write "{restore}if" if found */
2513 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2515 mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2516 mp_fatal_error(mp,s);
2520 static const char **check_cs_token_pair (MP mp) {
2521 const char **p = (const char **) cs_token_pairs_list;
2522 for (; p[0] != NULL; ++p)
2523 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2528 static void cs_store (MP mp, boolean is_subr) {
2532 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2533 *mp->ps->t1_buf_ptr++ = *p++);
2534 *mp->ps->t1_buf_ptr = 0;
2536 subr = (int)t1_scan_num (mp, p + 1, 0);
2538 ptr = mp->ps->subr_tab + subr;
2540 ptr = mp->ps->cs_ptr++;
2541 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2543 mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2544 mp_fatal_error(mp,s);
2546 if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
2547 ptr->glyph_name = (char *) notdef;
2549 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2551 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2552 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2553 (size_t) (mp->ps->t1_cslen + 4));
2554 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2555 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2556 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2557 *mp->ps->t1_buf_ptr++ = 10;
2558 if (is_subr && mp->ps->cs_token_pair == NULL)
2559 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2560 ptr->len = (unsigned short)(mp->ps->t1_buf_ptr - mp->ps->t1_buf_array);
2561 ptr->cslen = mp->ps->t1_cslen;
2562 ptr->data = mp_xmalloc (mp, (size_t)ptr->len , sizeof (byte));
2563 memcpy (ptr->data, mp->ps->t1_buf_array, (size_t)ptr->len);
2567 #define store_subr(mp) cs_store(mp,true)
2568 #define store_cs(mp) cs_store(mp,false)
2570 #define CC_STACK_SIZE 24
2572 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2573 static cc_entry cc_tab[CS_MAX];
2574 static boolean is_cc_init = false;
2578 if (stack_ptr - cc_stack < (N)) \
2582 #define stack_error(N) { \
2584 mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2585 (int) N, (int)(stack_ptr - cc_stack)); \
2591 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2593 #define cc_push(V) *stack_ptr++ = V
2594 #define cc_clear() stack_ptr = cc_stack
2596 #define set_cc(N, B, A, C) \
2597 cc_tab[N].nargs = A; \
2598 cc_tab[N].bottom = B; \
2599 cc_tab[N].clear = C; \
2600 cc_tab[N].valid = true
2602 static void cc_init (void) {
2606 for (i = 0; i < CS_MAX; i++)
2607 cc_tab[i].valid = false;
2608 set_cc (CS_HSTEM, true, 2, true);
2609 set_cc (CS_VSTEM, true, 2, true);
2610 set_cc (CS_VMOVETO, true, 1, true);
2611 set_cc (CS_RLINETO, true, 2, true);
2612 set_cc (CS_HLINETO, true, 1, true);
2613 set_cc (CS_VLINETO, true, 1, true);
2614 set_cc (CS_RRCURVETO, true, 6, true);
2615 set_cc (CS_CLOSEPATH, false, 0, true);
2616 set_cc (CS_CALLSUBR, false, 1, false);
2617 set_cc (CS_RETURN, false, 0, false);
2619 |set_cc(CS_ESCAPE, false, 0, false);|
2621 set_cc (CS_HSBW, true, 2, true);
2622 set_cc (CS_ENDCHAR, false, 0, true);
2623 set_cc (CS_RMOVETO, true, 2, true);
2624 set_cc (CS_HMOVETO, true, 1, true);
2625 set_cc (CS_VHCURVETO, true, 4, true);
2626 set_cc (CS_HVCURVETO, true, 4, true);
2627 set_cc (CS_DOTSECTION, false, 0, true);
2628 set_cc (CS_VSTEM3, true, 6, true);
2629 set_cc (CS_HSTEM3, true, 6, true);
2630 set_cc (CS_SEAC, true, 5, true);
2631 set_cc (CS_SBW, true, 4, true);
2632 set_cc (CS_DIV, false, 2, false);
2633 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2634 set_cc (CS_POP, false, 0, false);
2635 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2641 @d cs_getchar(mp) cdecrypt(*data++, &cr)
2643 @d mark_subr(mp,n) cs_mark(mp,0, n)
2644 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2645 @d SMALL_BUF_SIZE 256
2648 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2649 char buf[SMALL_BUF_SIZE];
2652 va_start (args, fmt);
2653 @= /*@@-bufferoverflowhigh@@*/ @>
2654 (void)vsprintf (buf, fmt, args);
2655 @= /*@@=bufferoverflowhigh@@*/ @>
2657 if (cs_name == NULL) {
2658 mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2660 mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2665 static void cs_mark (MP mp, const char *cs_name, int subr)
2671 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2675 if (cs_name == NULL) {
2677 ptr = mp->ps->subr_tab + subr;
2681 if (mp->ps->cs_notdef != NULL &&
2682 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2683 ptr = mp->ps->cs_notdef;
2685 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2686 if (strcmp (ptr->glyph_name, cs_name) == 0)
2688 if (ptr == mp->ps->cs_ptr) {
2690 mp_snprintf (s,128,"glyph `%s' undefined", cs_name);
2694 if (ptr->glyph_name == notdef)
2695 mp->ps->cs_notdef = ptr;
2698 /* only marked CharString entries and invalid entries can be skipped;
2699 valid marked subrs must be parsed to keep the stack in sync */
2700 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2702 ptr->is_used = true;
2704 cs_len = (int)ptr->cslen;
2705 data = ptr->data + 4;
2706 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2707 (void)cs_getchar (mp);
2708 while (cs_len > 0) {
2710 b = cs_getchar (mp);
2714 else if (b <= 250) {
2716 a = (int)((unsigned)(b - 247) << 8) + 108 + cs_getchar (mp);
2717 } else if (b <= 254) {
2719 a = -(int)((unsigned)(b - 251) << 8) - 108 - cs_getchar (mp);
2722 a = (cs_getchar (mp) & 0xff) << 24;
2723 a |= (cs_getchar (mp) & 0xff) << 16;
2724 a |= (cs_getchar (mp) & 0xff) << 8;
2725 a |= (cs_getchar (mp) & 0xff) << 0;
2726 if (sizeof (integer) > 4 && (a & 0x80000000))
2731 if (b == CS_ESCAPE) {
2732 b = cs_getchar (mp) + CS_1BYTE_MAX;
2736 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2742 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2746 if (stack_ptr - cc_stack < cc->nargs)
2747 cs_warn (mp,cs_name, subr,
2748 "less arguments on stack (%i) than required (%i)",
2749 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2750 else if (stack_ptr - cc_stack > cc->nargs)
2751 cs_warn (mp,cs_name, subr,
2752 "more arguments on stack (%i) than required (%i)",
2753 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2755 switch (cc - cc_tab) {
2760 if (!mp->ps->subr_tab[a1].valid) {
2761 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2769 case CS_CALLOTHERSUBR:
2770 if (cc_get (-1) == 3)
2771 lastargOtherSubr3 = cc_get (-3);
2772 a1 = cc_get (-2) + 2;
2776 cc_push (lastargOtherSubr3);
2777 /* the only case when we care about the value being pushed onto
2778 stack is when POP follows CALLOTHERSUBR (changing hints by
2786 mark_cs (mp,standard_glyph_names[a1]);
2787 mark_cs (mp,standard_glyph_names[a2]);
2796 cs_error: /* an error occured during parsing */
2799 ptr->is_used = false;
2802 static void t1_subset_ascii_part (MP mp, font_number tex_font, fm_entry *fm_cur)
2806 while (!t1_prefix ("/Encoding")) {
2807 t1_scan_param (mp,tex_font, fm_cur);
2811 t1_builtin_enc (mp);
2812 if (is_reencoded (fm_cur))
2813 mp->ps->t1_glyph_names = external_enc ();
2815 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2817 |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2818 make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2819 update_subset_tag ();
2822 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2823 t1_puts (mp,"/Encoding StandardEncoding def\n");
2826 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2827 for (i = 0, j = 0; i < 256; i++) {
2828 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2830 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
2831 "dup %i /%s put\n", (int) t1_char (i),
2832 mp->ps->t1_glyph_names[i]);
2833 t1_puts(mp,mp->ps->t1_line_array);
2836 /* We didn't mark anything for the Encoding array. */
2837 /* We add "dup 0 /.notdef put" for compatibility */
2838 /* with Acrobat 5.0. */
2840 t1_puts (mp,"dup 0 /.notdef put\n");
2841 t1_puts (mp,"readonly def\n");
2845 t1_scan_param (mp,tex_font, fm_cur);
2846 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
2849 while (mp->ps->t1_in_eexec == 0);
2852 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
2853 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
2855 static void cs_init (MP mp) {
2856 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2857 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2858 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2859 mp->ps->cs_token_pair = NULL;
2860 mp->ps->subr_tab = NULL;
2861 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2862 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2865 static void init_cs_entry ( cs_entry * cs) {
2867 cs->glyph_name = NULL;
2870 cs->is_used = false;
2874 static void t1_mark_glyphs (MP mp, font_number tex_font);
2876 static void t1_read_subrs (MP mp, font_number tex_font, fm_entry *fm_cur)
2881 while (!(t1_charstrings () || t1_subrs ())) {
2882 t1_scan_param (mp,tex_font, fm_cur);
2887 mp->ps->t1_cs = true;
2888 mp->ps->t1_scan = false;
2891 mp->ps->subr_size_pos = (int)(strlen ("/Subrs") + 1);
2892 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2893 mp->ps->subr_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2894 if (mp->ps->subr_size == 0) {
2895 while (!t1_charstrings ())
2899 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
2900 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,(size_t)mp->ps->subr_size, sizeof (cs_entry));
2901 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2902 init_cs_entry (ptr);
2903 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2905 while (mp->ps->t1_cslen) {
2909 /* mark the first four entries without parsing */
2910 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2911 mp->ps->subr_tab[i].is_used = true;
2912 /* the end of the Subrs array might have more than one line so we need to
2913 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2914 the Subrs array followed by the CharStrings dict immediately (synthetic
2915 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2916 we will treat the font as synthetic and ignore everything until next
2919 #define POST_SUBRS_SCAN 5
2921 *mp->ps->t1_buf_array = 0;
2922 for (i = 0; i < POST_SUBRS_SCAN; i++) {
2923 if (t1_charstrings ())
2925 s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2926 alloc_array (t1_buf, s, T1_BUF_SIZE);
2927 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2930 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2931 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2932 suppose synthetic font */
2933 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2935 mp_xfree (ptr->data);
2936 mp_xfree (mp->ps->subr_tab);
2937 mp_xfree (mp->ps->subr_array_start);
2938 mp_xfree (mp->ps->subr_array_end);
2940 mp->ps->t1_cs = false;
2941 mp->ps->t1_synthetic = true;
2942 while (!(t1_charstrings () || t1_subrs ()))
2949 static void t1_flush_cs (MP mp, boolean is_subr)
2952 byte *r, *return_cs = NULL;
2953 cs_entry *tab, *end_tab, *ptr;
2954 char *start_line, *line_end;
2955 int count, size_pos;
2956 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2958 start_line = mp->ps->subr_array_start;
2959 line_end = mp->ps->subr_array_end;
2960 size_pos = mp->ps->subr_size_pos;
2961 tab = mp->ps->subr_tab;
2962 count = mp->ps->subr_max + 1;
2963 end_tab = mp->ps->subr_tab + count;
2965 start_line = mp->ps->cs_dict_start;
2966 line_end = mp->ps->cs_dict_end;
2967 size_pos = mp->ps->cs_size_pos;
2968 tab = mp->ps->cs_tab;
2969 end_tab = mp->ps->cs_ptr;
2970 count = mp->ps->cs_count;
2972 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2973 for (p = start_line; p - start_line < size_pos;)
2974 *mp->ps->t1_line_ptr++ = *p++;
2975 while (mp_isdigit (*p))
2977 mp_snprintf (mp->ps->t1_line_ptr, (int)mp->ps->t1_line_limit, "%u", (unsigned)count);
2978 strcat (mp->ps->t1_line_ptr, p);
2979 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2982 /* create |return_cs| to replace unsused subr's */
2986 return_cs = mp_xmalloc (mp, (size_t)(mp->ps->t1_lenIV + 1) , sizeof(byte));
2987 if ( mp->ps->t1_lenIV > 0) {
2988 for (cs_len = 0, r = return_cs;
2989 cs_len<(unsigned short)mp->ps->t1_lenIV; cs_len++, r++)
2990 *r = cencrypt (0x00, &cr);
2991 *r = cencrypt (CS_RETURN, &cr);
2993 *return_cs = CS_RETURN;
2998 for (ptr = tab; ptr < end_tab; ptr++) {
3001 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3002 "dup %i %u", (int) (ptr - tab), ptr->cslen);
3004 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3005 "/%s %u", ptr->glyph_name, ptr->cslen);
3006 p = strend (mp->ps->t1_line_array);
3007 memcpy (p, ptr->data, (size_t)ptr->len);
3008 mp->ps->t1_line_ptr = p + ptr->len;
3011 /* replace unsused subr's by |return_cs| */
3013 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3014 "dup %i %u%s ", (int) (ptr - tab),
3015 cs_len, mp->ps->cs_token_pair[0]);
3016 p = strend (mp->ps->t1_line_array);
3017 memcpy (p, return_cs, (size_t)cs_len);
3018 mp->ps->t1_line_ptr = p + cs_len;
3020 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3021 " %s", mp->ps->cs_token_pair[1]);
3022 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3026 mp_xfree (ptr->data);
3027 if (ptr->glyph_name != notdef)
3028 mp_xfree (ptr->glyph_name);
3030 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "%s", line_end);
3031 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3034 mp_xfree (return_cs);
3036 mp_xfree (start_line);
3037 mp_xfree (line_end);
3040 static void t1_mark_glyphs (MP mp, font_number tex_font)
3043 char *charset = extra_charset ();
3046 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
3047 if (mp->ps->cs_tab != NULL)
3048 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3050 ptr->is_used = true;
3051 if (mp->ps->subr_tab != NULL) {
3052 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3054 ptr->is_used = true;
3055 mp->ps->subr_max = mp->ps->subr_size - 1;
3059 mark_cs (mp,notdef);
3060 for (i = 0; i < 256; i++)
3061 if (is_used_char (i)) {
3062 if (mp->ps->t1_glyph_names[i] == notdef) {
3064 mp_snprintf(S,128, "character %i is mapped to %s", i, notdef);
3067 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3069 if (charset == NULL)
3071 g = s = charset + 1; /* skip the first '/' */
3074 while (*s != '/' && s < r)
3076 *s = 0; /* terminate g by rewriting '/' to 0 */
3081 if (mp->ps->subr_tab != NULL)
3082 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3083 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3085 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3086 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3089 static void t1_subset_charstrings (MP mp, font_number tex_font)
3092 mp->ps->cs_size_pos =
3093 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3094 - mp->ps->t1_line_array + 1;
3095 /* |cs_size_pos| points to the number indicating
3096 dict size after "/CharStrings" */
3097 mp->ps->cs_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3098 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,(size_t)mp->ps->cs_size, sizeof(cs_entry));
3099 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3100 init_cs_entry (ptr);
3101 mp->ps->cs_notdef = NULL;
3102 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3104 while (mp->ps->t1_cslen) {
3108 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3109 t1_mark_glyphs (mp,tex_font);
3110 if (mp->ps->subr_tab != NULL) {
3111 if (mp->ps->cs_token_pair == NULL)
3113 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3116 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3122 static void t1_subset_end (MP mp)
3124 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3125 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3129 while (!t1_end_eexec ())
3130 t1_getline (mp); /* ignore the rest */
3131 t1_putline (mp); /* write "mark currentfile closefile" */
3133 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3138 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3139 while (!t1_cleartomark ()) {
3143 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3144 t1_check_end (mp); /* write "{restore}if" if found */
3148 static int t1_updatefm (MP mp, font_number f, fm_entry *fm)
3151 mp->ps->read_encoding_only = true;
3152 if (!t1_open_fontfile (mp,fm,NULL)) {
3155 t1_scan_only (mp,f, fm);
3156 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3158 while (*p != ' ' && *p != 0)
3162 t1_close_font_file (mp,"");
3167 static void writet1 (MP mp, font_number tex_font, fm_entry *fm_cur) {
3168 unsigned save_selector = mp->selector;
3169 mp_normalize_selector(mp);
3170 mp->ps->read_encoding_only = false;
3171 if (!is_included (fm_cur)) { /* scan parameters from font file */
3172 if (!t1_open_fontfile (mp,fm_cur,"{"))
3174 t1_scan_only (mp,tex_font, fm_cur);
3175 t1_close_font_file (mp,"}");
3178 if (!is_subsetted (fm_cur)) { /* include entire font */
3179 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3181 t1_include (mp,tex_font,fm_cur);
3182 t1_close_font_file (mp,">>");
3185 /* partial downloading */
3186 if (!t1_open_fontfile (mp,fm_cur,"<"))
3188 t1_subset_ascii_part (mp,tex_font,fm_cur);
3189 t1_start_eexec (mp,fm_cur);
3192 t1_read_subrs (mp,tex_font, fm_cur);
3193 t1_subset_charstrings (mp,tex_font);
3195 t1_close_font_file (mp,">");
3196 mp->selector = save_selector;
3200 static void t1_free (MP mp);
3203 static void t1_free (MP mp) {
3204 mp_xfree (mp->ps->t1_line_array);
3205 mp_xfree (mp->ps->t1_buf_array);
3209 @* \[44d] Embedding fonts.
3211 @ The |tfm_num| is officially of type |font_number|, but that
3212 type does not exist yet at this point in the output order.
3216 char *tfm_name; /* TFM file name */
3217 char *ps_name; /* PostScript name */
3218 integer flags; /* font flags */
3219 char *ff_name; /* font file name */
3220 char *subset_tag; /* pseudoUniqueTag for subsetted font */
3221 enc_entry *encoding; /* pointer to corresponding encoding */
3222 unsigned int tfm_num; /* number of the TFM refering this entry */
3223 unsigned short type; /* font type (T1/TTF/...) */
3224 short slant; /* SlantFont */
3225 short extend; /* ExtendFont */
3226 integer ff_objnum; /* FontFile object number */
3227 integer fn_objnum; /* FontName/BaseName object number */
3228 integer fd_objnum; /* FontDescriptor object number */
3229 char *charset; /* string containing used glyphs */
3230 boolean all_glyphs; /* embed all glyphs? */
3231 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
3232 short tfm_avail; /* flags whether a tfm is available */
3233 short pid; /* Pid for truetype fonts */
3234 short eid; /* Eid for truetype fonts */
3240 #define FONTNAME_BUF_SIZE 128
3241 boolean fontfile_found;
3242 boolean is_otf_font;
3243 char fontname_buf[FONTNAME_BUF_SIZE];
3251 @d set_included(fm) ((fm)->type |= F_INCLUDED)
3252 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
3253 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
3254 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
3256 @d is_included(fm) ((fm)->type & F_INCLUDED)
3257 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
3258 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
3259 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
3260 @d is_reencoded(fm) ((fm)->encoding != NULL)
3261 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
3262 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
3264 @d fm_slant(fm) (fm)->slant
3265 @d fm_extend(fm) (fm)->extend
3266 @d fm_fontfile(fm) (fm)->ff_name
3269 static boolean mp_font_is_reencoded (MP mp, font_number f);
3270 static boolean mp_font_is_included (MP mp, font_number f);
3271 static boolean mp_font_is_subsetted (MP mp, font_number f);
3274 boolean mp_font_is_reencoded (MP mp, font_number f) {
3276 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3278 && (fm->ps_name != NULL)
3279 && is_reencoded (fm))
3284 boolean mp_font_is_included (MP mp, font_number f) {
3286 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3288 && (fm->ps_name != NULL && fm->ff_name != NULL)
3289 && is_included (fm))
3294 boolean mp_font_is_subsetted (MP mp, font_number f) {
3296 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
3298 && (fm->ps_name != NULL && fm->ff_name != NULL)
3299 && is_included (fm) && is_subsetted (fm))
3306 static char * mp_fm_encoding_name (MP mp, font_number f);
3307 static char * mp_fm_font_name (MP mp, font_number f);
3308 static char * mp_fm_font_subset_name (MP mp, font_number f);
3311 @c char * mp_fm_encoding_name (MP mp, font_number f) {
3314 if (mp_has_fm_entry (mp, f, &fm)) {
3315 if (fm != NULL && (fm->ps_name != NULL)) {
3316 if (is_reencoded (fm)) {
3318 if (e->enc_name!=NULL)
3319 return mp_xstrdup(mp,e->enc_name);
3325 print_err ("fontmap encoding problems for font ");
3326 mp_print(mp,mp->font_name[f]);
3330 char * mp_fm_font_name (MP mp, font_number f) {
3332 if (mp_has_fm_entry (mp, f,&fm)) {
3333 if (fm != NULL && (fm->ps_name != NULL)) {
3334 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3335 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3336 if (t1_updatefm(mp,f,fm)) {
3337 mp->font_ps_name_fixed[f] = true;
3339 print_err ("font loading problems for font ");
3340 mp_print(mp,mp->font_name[f]);
3344 return mp_xstrdup(mp,fm->ps_name);
3347 print_err ("fontmap name problems for font ");
3348 mp_print(mp,mp->font_name[f]);
3353 static char * mp_fm_font_subset_name (MP mp, font_number f) {
3355 if (mp_has_fm_entry (mp, f, &fm)) {
3356 if (fm != NULL && (fm->ps_name != NULL)) {
3357 if (is_subsetted(fm)) {
3358 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3359 mp_snprintf(s,(int)strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3362 return mp_xstrdup(mp,fm->ps_name);
3366 print_err ("fontmap name problems for font ");
3367 mp_print(mp,mp->font_name[f]);
3373 static integer mp_fm_font_slant (MP mp, font_number f);
3374 static integer mp_fm_font_extend (MP mp, font_number f);
3377 @c static integer mp_fm_font_slant (MP mp, font_number f) {
3379 if (mp_has_fm_entry (mp, f, &fm)) {
3380 if (fm != NULL && (fm->ps_name != NULL)) {
3386 static integer mp_fm_font_extend (MP mp, font_number f) {
3388 if (mp_has_fm_entry (mp, f, &fm)) {
3389 if (fm != NULL && (fm->ps_name != NULL)) {
3397 static boolean mp_do_ps_font (MP mp, font_number f);
3399 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3401 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3404 if (is_truetype(fm_cur) ||
3405 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3408 if (is_included(fm_cur)) {
3409 mp_ps_print_nl(mp,"%%BeginResource: font ");
3410 if (is_subsetted(fm_cur)) {
3411 mp_ps_print(mp, fm_cur->subset_tag);
3412 mp_ps_print_char(mp,'-');
3414 mp_ps_print(mp, fm_cur->ps_name);
3416 writet1 (mp,f,fm_cur);
3417 mp_ps_print_nl(mp,"%%EndResource");
3423 @ Included subset fonts do not need and encoding vector, make
3424 sure we skip that case.
3427 static void mp_list_used_resources (MP mp, int prologues, int procset);
3429 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
3430 font_number f; /* fonts used in a text node or as loop counters */
3431 int ff; /* a loop counter */
3432 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3435 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
3437 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3440 for (f=null_font+1;f<=mp->last_fnum;f++) {
3441 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3442 for (ff=ldf;ff>=null_font;ff--) {
3443 if ( mp_has_font_size(mp,(font_number)ff) )
3444 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3447 if ( mp_font_is_subsetted(mp,f) )
3449 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
3450 (size_t)mp->max_print_line )
3451 mp_ps_print_nl(mp, "%%+ encoding");
3454 mp_ps_print_nl(mp, "%%+ encoding");
3456 mp_ps_print_char(mp, ' ');
3457 mp_ps_print(mp, mp->font_enc_name[f]);
3465 for (f=null_font+1;f<=mp->last_fnum;f++) {
3466 if ( mp_has_font_size(mp,f) ) {
3467 for (ff=ldf;ff>=null_font;ff--) {
3468 if ( mp_has_font_size(mp,(font_number)ff) )
3469 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3472 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
3473 (size_t)mp->max_print_line )
3474 mp_ps_print_nl(mp, "%%+ font");
3477 mp_ps_print_nl(mp, "%%+ font");
3479 mp_ps_print_char(mp, ' ');
3480 if ( (prologues==3)&&
3481 (mp_font_is_subsetted(mp,f)) )
3482 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3484 mp_ps_print(mp, mp->font_ps_name[f]);
3494 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
3496 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
3497 font_number f; /* fonts used in a text node or as loop counters */
3498 int ff; /* a loop counter */
3499 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3502 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3504 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3507 for (f=null_font+1;f<=mp->last_fnum;f++) {
3508 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3509 for (ff=ldf;ff>= null_font;ff++) {
3510 if ( mp_has_font_size(mp,(font_number)ff) )
3511 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3514 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3516 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(size_t)mp->max_print_line )
3517 mp_ps_print_nl(mp, "%%+ encoding");
3520 mp_ps_print_nl(mp, "%%+ encoding");
3522 mp_ps_print_char(mp, ' ');
3523 mp_ps_print(mp, mp->font_enc_name[f]);
3532 for (f=null_font+1;f<=mp->last_fnum;f++) {
3533 if ( mp_has_font_size(mp,f) ) {
3534 for (ff=ldf;ff>= null_font;ff--) {
3535 if ( mp_has_font_size(mp,(font_number)ff) )
3536 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3539 if ( ! mp_font_is_included(mp,f) )
3541 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
3542 mp_ps_print_nl(mp, "%%+ font");
3545 mp_ps_print_nl(mp, "%%+ font");
3547 mp_ps_print_char(mp, ' ');
3548 if ( mp_font_is_subsetted(mp,f) )
3549 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3551 mp_ps_print(mp, mp->font_ps_name[f]);
3561 @ @<Declarations...@>=
3562 static void mp_list_needed_resources (MP mp, int prologues);
3564 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
3565 font_number f; /* fonts used in a text node or as loop counters */
3566 int ff; /* a loop counter */
3567 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3571 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3572 if ( mp_has_font_size(mp,f)) {
3573 for (ff=ldf;ff>=null_font;ff--) {
3574 if ( mp_has_font_size(mp,(font_number)ff) )
3575 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3578 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3580 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
3581 mp_ps_print_nl(mp, "%%+ font");
3584 mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
3586 mp_ps_print_char(mp, ' ');
3587 mp_ps_print(mp, mp->font_ps_name[f]);
3593 if ( ! firstitem ) {
3597 for (f=null_font+1;f<= mp->last_fnum;f++) {
3598 if ( mp_has_font_size(mp,f) ) {
3599 for (ff=ldf;ff>=null_font;ff-- ) {
3600 if ( mp_has_font_size(mp,(font_number)ff) )
3601 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3604 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3606 mp_ps_print(mp, "%%IncludeResource: font ");
3607 mp_ps_print(mp, mp->font_ps_name[f]);
3618 static void mp_write_font_definition (MP mp, font_number f, int prologues);
3622 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3623 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3625 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
3626 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3627 (mp_fm_font_extend(mp,f)!=0)||
3628 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3629 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3630 if ( (mp_font_is_subsetted(mp,f))&&
3631 (mp_font_is_included(mp,f))&&(prologues==3))
3632 mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3634 mp_ps_name_out(mp, mp->font_ps_name[f],true);
3635 mp_ps_print(mp, " fcp");
3637 if ( applied_reencoding(f) ) {
3638 mp_ps_print(mp, "/Encoding ");
3639 mp_ps_print(mp, mp->font_enc_name[f]);
3640 mp_ps_print(mp, " def ");
3642 if ( mp_fm_font_slant(mp,f)!=0 ) {
3643 mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
3644 mp_ps_print(mp, " SlantFont ");
3646 if ( mp_fm_font_extend(mp,f)!=0 ) {
3647 mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3648 mp_ps_print(mp, " ExtendFont ");
3650 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3651 mp_ps_print(mp, " 890 ScaleFont ");
3652 mp_ps_print(mp, " 277 SlantFont ");
3654 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3655 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3656 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3657 mp_ps_print(mp, "/space[0 -278]def ");
3658 mp_ps_print(mp, "/a12[-904 -939]def ");
3659 mp_ps_print(mp, "end def ");
3661 mp_ps_print(mp, "currentdict end");
3663 mp_ps_print_defined_name(mp,f,prologues);
3664 mp_ps_print(mp, " exch definefont pop");
3670 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
3673 @c static void mp_ps_print_defined_name(MP mp, font_number f, int prologues) {
3674 mp_ps_print(mp, " /");
3675 if ((mp_font_is_subsetted(mp,f))&&
3676 (mp_font_is_included(mp,f))&&(prologues==3))
3677 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3679 mp_ps_print(mp, mp->font_ps_name[f]);
3680 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 )
3681 mp_ps_print(mp, "-Slanted");
3682 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 )
3683 mp_ps_print(mp, "-Reverse");
3684 if ( applied_reencoding(f) ) {
3685 mp_ps_print(mp, "-");
3686 mp_ps_print(mp, mp->font_enc_name[f]);
3688 if ( mp_fm_font_slant(mp,f)!=0 ) {
3689 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,f)) ;
3691 if ( mp_fm_font_extend(mp,f)!=0 ) {
3692 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3696 @ @<Include encodings and fonts for edge structure~|h|@>=
3697 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
3698 @<Embed fonts that are available@>
3700 @ @<Embed fonts that are available@>=
3703 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3706 for (f=null_font+1;f<=mp->last_fnum;f++) {
3707 if ( cur_fsize[f]!=null ) {
3708 if (prologues==3 ) {
3709 if ( ! mp_do_ps_font(mp,f) ) {
3710 if ( mp_has_fm_entry(mp,f, NULL) ) {
3711 print_err("Font embedding failed");
3716 cur_fsize[f]=link(cur_fsize[f]);
3717 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3721 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3723 } while (! done_fonts);
3726 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3729 mp_apply_mark_string_chars(mp, h, next_size);
3732 @ We also need to keep track of which characters are used in text nodes
3733 in the edge structure that is being shipped out. This is done by procedures
3734 that use the left-over |b3| field in the |char_info| words; i.e.,
3735 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3738 enum mp_char_mark_state {mp_unused=0, mp_used};
3741 static void mp_mark_string_chars (MP mp,font_number f, char *s) ;
3744 void mp_mark_string_chars (MP mp,font_number f, char *s) {
3745 integer b; /* |char_base[f]| */
3746 ASCII_code bc,ec; /* only characters between these bounds are marked */
3747 char *k; /* an index into string |s| */
3753 if ( (*k>=(char)bc)&&(*k<=(char)ec) )
3754 mp->font_info[b+*k].qqqq.b3=mp_used;
3761 static void mp_unmark_font (MP mp,font_number f) ;
3764 void mp_unmark_font (MP mp,font_number f) {
3765 int k; /* an index into |font_info| */
3766 for (k= mp->char_base[f]+mp->font_bc[f];
3767 k<=mp->char_base[f]+mp->font_ec[f];
3769 mp->font_info[k].qqqq.b3=mp_unused;
3774 static void mp_print_improved_prologue (MP mp, mp_edge_object *h, int p1, int procset) ;
3777 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
3778 quarterword next_size; /* the size index for fonts being listed */
3779 pointer *cur_fsize; /* current positions in |font_sizes| */
3780 boolean done_fonts; /* have we finished listing the fonts in the header? */
3781 font_number f; /* a font number for loops */
3782 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer));
3783 mp_list_used_resources(mp, prologues, procset);
3784 mp_list_supplied_resources(mp, prologues, procset);
3785 mp_list_needed_resources(mp, prologues);
3786 mp_ps_print_nl(mp, "%%EndComments");
3787 mp_ps_print_nl(mp, "%%BeginProlog");
3789 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
3791 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3792 mp_ps_print_nl(mp, "/bd{bind def}bind def"
3793 "/fshow {exch findfont exch scalefont setfont show}bd");
3794 if ( procset>0 ) @<Print the procset@>;
3795 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
3796 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3797 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3798 "/fmd{/FontMatrix exch def}bd");
3799 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3800 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3801 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3802 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3803 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3804 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3805 mp_ps_print_nl(mp, "%%EndResource");
3806 @<Include encodings and fonts for edge structure~|h|@>;
3807 mp_ps_print_nl(mp, "%%EndProlog");
3808 mp_ps_print_nl(mp, "%%BeginSetup");
3810 for (f=null_font+1;f<=mp->last_fnum;f++) {
3811 if ( mp_has_font_size(mp,f) ) {
3812 if ( mp_has_fm_entry(mp,f,NULL) ) {
3813 mp_write_font_definition(mp,f, prologues);
3814 mp_ps_name_out(mp, mp->font_name[f],true);
3815 mp_ps_print_defined_name(mp,f, prologues);
3816 mp_ps_print(mp, " def");
3819 mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3821 mp_ps_name_out(mp, mp->font_name[f],true);
3822 mp_ps_name_out(mp, mp->font_name[f],true);
3823 mp_ps_print(mp, " def");
3828 mp_ps_print_nl(mp, "%%EndSetup");
3829 mp_ps_print_nl(mp, "%%Page: 1 1");
3831 mp_xfree(cur_fsize);
3835 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues);
3840 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) {
3841 quarterword next_size; /* the size index for fonts being listed */
3842 pointer *cur_fsize; /* current positions in |font_sizes| */
3843 int ff; /* a loop counter */
3844 boolean done_fonts; /* have we finished listing the fonts in the header? */
3845 font_number f; /* a font number for loops */
3846 scaled ds; /* design size and scale factor for a text node */
3847 int ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3848 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer));
3849 if ( prologues>0 ) {
3850 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3851 |font_sizes| and eliminate duplicates@>;
3854 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3855 do { done_fonts=true;
3856 for (f=null_font+1;f<=mp->last_fnum;f++) {
3857 if ( cur_fsize[f]!=null ) {
3858 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3860 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
3862 if ( ! done_fonts ) {
3863 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3866 } while (! done_fonts);
3868 mp_xfree(cur_fsize);
3869 return (font_number)ldf;
3872 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3873 for (f=null_font+1;f<= mp->last_fnum;f++)
3874 cur_fsize[f]=mp->font_sizes[f]
3876 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3877 so we carefully remove duplicates. There is no harm in using a slow, brute-force
3880 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3883 for (f=null_font+1;f<= mp->last_fnum;f++) {
3884 if ( mp->font_sizes[f]!=null ) {
3885 if ( ldf==null_font )
3886 mp_ps_print_nl(mp, "%%DocumentFonts:");
3887 for (ff=ldf;ff>=null_font;ff--) {
3888 if ( mp->font_sizes[ff]!=null )
3889 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3892 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
3893 mp_ps_print_nl(mp, "%%+");
3894 mp_ps_print_char(mp, ' ');
3895 mp_ps_print(mp, mp->font_ps_name[f]);
3904 static void mp_hex_digit_out (MP mp,quarterword d) {
3905 if ( d<10 ) mp_ps_print_char(mp, d+'0');
3906 else mp_ps_print_char(mp, d+'a'-10);
3909 @ We output the marks as a hexadecimal bit string starting at |c| or
3910 |font_bc[f]|, whichever is greater. If the output has to be truncated
3911 to avoid exceeding |emergency_line_length| the return value says where to
3912 start scanning next time.
3915 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3918 @d emergency_line_length 255
3919 /* \ps\ output lines can be this long in unusual circumstances */
3922 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3923 eight_bits bc,ec; /* only encode characters between these bounds */
3924 integer lim; /* the maximum number of marks to encode before truncating */
3925 int p; /* |font_info| index for the current character */
3926 int d; /* used to construct a hexadecimal digit */
3927 unsigned b; /* used to construct a hexadecimal digit */
3928 lim=4*(emergency_line_length-mp->ps->ps_offset-4);
3932 @<Restrict the range |bc..ec| so that it contains no unused characters
3933 at either end and has length at most |lim|@>;
3934 @<Print the initial label indicating that the bitmap starts at |bc|@>;
3935 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3936 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3942 @ We could save time by setting the return value before the loop that
3943 decrements |ec|, but there is no point in being so tricky.
3945 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3946 p=mp->char_base[f]+bc;
3947 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3950 if ( ec>=(eight_bits)(bc+lim)) ec=(eight_bits)(bc+lim-1);
3951 p=mp->char_base[f]+ec;
3952 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3956 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3957 mp_ps_print_char(mp, ' ');
3958 mp_hex_digit_out(mp, (quarterword)bc / 16);
3959 mp_hex_digit_out(mp, (quarterword)bc % 16);
3960 mp_ps_print_char(mp, ':')
3964 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3966 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3968 mp_hex_digit_out(mp, (quarterword)d);
3971 if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
3974 mp_hex_digit_out(mp, (quarterword)d)
3977 @ Here is a simple function that determines whether there are any marked
3978 characters in font~|f| with character code at least~|c|.
3981 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
3984 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
3985 int p; /* |font_info| index for the current character */
3986 for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
3987 if ( mp->font_info[p].qqqq.b3==mp_used )
3994 @ If the file name is so long that it can't be printed without exceeding
3995 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
3996 line. We might have to repeat line in order to get the character usage
3997 information to fit within |emergency_line_length|.
3999 TODO: these two defines are also defined in mp.w!
4001 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
4002 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
4004 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
4006 while ( mp_check_ps_marks(mp, f,t) ) {
4007 mp_ps_print_nl(mp, "%*Font: ");
4008 if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
4010 mp_ps_print(mp, mp->font_name[f]);
4011 mp_ps_print_char(mp, ' ');
4012 ds=(mp->font_dsize[f] + 8) / 16;
4013 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
4014 if ( mp->ps->ps_offset+12>emergency_line_length ) break;
4015 mp_ps_print_char(mp, ' ');
4016 mp_ps_print_scaled(mp, ds);
4017 if ( mp->ps->ps_offset+5>emergency_line_length ) break;
4018 t=mp_ps_marks_out(mp, f, (eight_bits)t);
4020 cur_fsize[f]=link(cur_fsize[f]);
4023 @ @<Print the procset@>=
4025 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
4026 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
4027 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
4028 "/p{closepath}bd/n{newpath}bd");
4029 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
4030 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
4031 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
4032 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
4033 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4037 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4038 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
4039 might not have reasonable |font_ps_name| entries, but we just charge ahead
4040 anyway. The user should not make \&{prologues} positive if this will cause
4042 @:prologues_}{\&{prologues} primitive@>
4045 static void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset);
4048 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
4051 ldf = mp_print_font_comments (mp, h, prologues);
4053 if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
4054 mp_read_psname_table(mp);
4055 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4056 if ( (prologues>0)||(procset>0) ) {
4057 if ( ldf!=null_font ) {
4058 if ( prologues>0 ) {
4059 for (f=null_font+1;f<=mp->last_fnum;f++) {
4060 if ( mp_has_font_size(mp,f) ) {
4061 mp_ps_name_out(mp, mp->font_name[f],true);
4062 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4063 mp_ps_print(mp, " def");
4068 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4074 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4075 if ( (prologues>0)&&(ldf!=null_font) )
4077 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4079 mp_ps_print_nl(mp, "/bd{bind def}bind def");
4080 @<Print the procset@>;
4081 mp_ps_print_nl(mp, "%%EndResource");
4085 mp_ps_print(mp, "%%EndProlog");
4086 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4089 @ \MP\ used to have one single routine to print to both `write' files
4090 and the PostScript output. Web2c redefines ``Character |k| cannot be
4091 printed'', and that resulted in some bugs where 8-bit characters were
4092 written to the PostScript file (reported by Wlodek Bzyl).
4094 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4095 a plain space, since that makes it easier to parse the result file
4098 @<Character |k| is not allowed in PostScript output@>=
4101 @ We often need to print a pair of coordinates.
4104 void mp_ps_pair_out (MP mp,scaled x, scaled y) {
4106 mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
4107 mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
4111 static void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4114 void mp_ps_print_cmd (MP mp, const char *l, const char *s) {
4115 if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4116 else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4120 static void mp_ps_print_cmd (MP mp, const char *l, const char *s) ;
4123 void mp_ps_string_out (MP mp, const char *s) {
4124 ASCII_code k; /* bits to be converted to octal */
4125 mp_ps_print(mp, "(");
4126 while ((k=(ASCII_code)*s++)) {
4127 if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4128 mp_ps_print_char(mp, '\\');
4131 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4132 mp_ps_print_char(mp, '\\');
4133 mp_ps_print_char(mp, '0'+(k / 64));
4134 mp_ps_print_char(mp, '0'+((k / 8) % 8));
4135 mp_ps_print_char(mp, '0'+(k % 8));
4137 if ( (k=='(')||(k==')')||(k=='\\') )
4138 mp_ps_print_char(mp, '\\');
4139 mp_ps_print_char(mp, k);
4142 mp_ps_print_char(mp, ')');
4146 static void mp_ps_string_out (MP mp, const char *s) ;
4148 @ This is a define because the function does not use its |mp| argument.
4150 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4153 static boolean mp_do_is_ps_name (char *s) {
4154 ASCII_code k; /* the character being checked */
4155 while ((k=(ASCII_code)*s++)) {
4156 if ( (k<=' ')||(k>'~') ) return false;
4157 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4158 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4164 static void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4167 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4168 ps_room(strlen(s)+2);
4169 mp_ps_print_char(mp, ' ');
4170 if ( mp_is_ps_name(mp, s) ) {
4171 if ( lit ) mp_ps_print_char(mp, '/');
4174 mp_ps_string_out(mp, s);
4175 if ( ! lit ) mp_ps_print(mp, "cvx ");
4176 mp_ps_print(mp, "cvn");
4181 @ These special comments described in the {\sl PostScript Language Reference
4182 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4183 We can't normally output ``conforming'' \ps\ because
4184 the structuring conventions don't allow us to say ``Please make sure the
4185 following characters are downloaded and define the \.{fshow} macro to access
4188 The exact bounding box is written out if |mp_prologues<0|, although this
4189 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4190 accurately. (Overfull boxes are avoided if an illustration is made to
4191 match a given \.{\char`\\hsize}.)
4194 static void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues);
4197 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) {
4199 mp_ps_print(mp, "%!PS");
4201 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
4202 mp_ps_print_nl(mp, "%%BoundingBox: ");
4203 if ( hh->minx>hh->maxx) {
4204 mp_ps_print(mp, "0 0 0 0");
4205 } else if ( prologues<0 ) {
4206 mp_ps_pair_out(mp, hh->minx,hh->miny);
4207 mp_ps_pair_out(mp, hh->maxx,hh->maxy);
4209 mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->minx),mp_floor_scaled(mp, hh->miny));
4210 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->maxx),-mp_floor_scaled(mp, -hh->maxy));
4212 mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
4213 if ( hh->minx>hh->maxx ) {
4214 mp_ps_print(mp, "0 0 0 0");
4216 mp_ps_pair_out(mp, hh->minx,hh->miny);
4217 mp_ps_pair_out(mp, hh->maxx,hh->maxy);
4219 mp_ps_print_nl(mp, "%%Creator: MetaPost ");
4220 mp_ps_print(mp, mp_metapost_version());
4221 mp_ps_print_nl(mp, "%%CreationDate: ");
4222 mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
4223 mp_ps_print_char(mp, '.');
4224 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month]));
4225 mp_ps_print_char(mp, '.');
4226 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
4227 mp_ps_print_char(mp, ':');
4228 t=mp_round_unscaled(mp, mp->internal[mp_time]);
4229 mp_ps_print_dd(mp, t / 60);
4230 mp_ps_print_dd(mp, t % 60);
4231 mp_ps_print_nl(mp, "%%Pages: 1");
4234 @ The most important output procedure is the one that gives the \ps\ version of
4238 typedef struct mp_knot {
4239 unsigned short left_type;
4240 unsigned short right_type;
4247 struct mp_knot * next;
4248 unsigned char originator;
4252 #define gr_left_type(A) (A)->left_type
4253 #define gr_right_type(A) (A)->right_type
4254 #define gr_x_coord(A) (A)->x_coord
4255 #define gr_y_coord(A) (A)->y_coord
4256 #define gr_left_x(A) (A)->left_x
4257 #define gr_left_y(A) (A)->left_y
4258 #define gr_right_x(A) (A)->right_x
4259 #define gr_right_y(A) (A)->right_y
4260 #define gr_next_knot(A) (A)->next
4261 #define gr_originator(A) (A)->originator
4263 @ If we want to duplicate a knot node, we can say |copy_knot|:
4266 static mp_knot *mp_gr_copy_knot (MP mp, mp_knot *p) {
4267 mp_knot *q; /* the copy */
4268 q = mp_xmalloc(mp, 1, sizeof (mp_knot));
4269 memcpy(q,p,sizeof (mp_knot));
4270 gr_next_knot(q)=NULL;
4274 @ The |copy_path| routine makes a clone of a given path.
4277 static mp_knot *mp_gr_copy_path (MP mp, mp_knot *p) {
4278 mp_knot *q, *pp, *qq; /* for list manipulation */
4281 q=mp_gr_copy_knot(mp, p);
4285 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4286 qq=gr_next_knot(qq);
4287 pp=gr_next_knot(pp);
4293 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4294 returns a pointer to the first node of the copy, if the path is a cycle,
4295 but to the final node of a non-cyclic copy. The global
4296 variable |path_tail| will point to the final node of the original path;
4297 this trick makes it easier to implement `\&{doublepath}'.
4299 All node types are assumed to be |endpoint| or |explicit| only.
4301 This function is currenly unused.
4304 mp_knot * mp_gr_htap_ypoc (MP mp, mp_knot *p) {
4305 mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4306 q=mp_xmalloc(mp, 1, sizeof (mp_knot)); /* this will correspond to |p| */
4309 gr_right_type(qq)=gr_left_type(pp);
4310 gr_left_type(qq)=gr_right_type(pp);
4311 gr_x_coord(qq)=gr_x_coord(pp);
4312 gr_y_coord(qq)=gr_y_coord(pp);
4313 gr_right_x(qq)=gr_left_x(pp);
4314 gr_right_y(qq)=gr_left_y(pp);
4315 gr_left_x(qq)=gr_right_x(pp);
4316 gr_left_y(qq)=gr_right_y(pp);
4317 gr_originator(qq)=gr_originator(pp);
4318 if ( gr_next_knot(pp)==p ) {
4320 /* |mp->path_tail=pp;| */ /* ? */
4323 rr=mp_xmalloc(mp, 1, sizeof (mp_knot));
4324 gr_next_knot(rr)=qq;
4326 pp=gr_next_knot(pp);
4330 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4331 calling the following subroutine.
4334 static void mp_do_gr_toss_knot_list (mp_knot *p) ;
4337 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4340 void mp_do_gr_toss_knot_list (mp_knot * p) {
4341 mp_knot *q; /* the node being freed */
4342 mp_knot *r; /* the next node */
4355 static void mp_gr_ps_path_out (MP mp, mp_knot *h) {
4356 mp_knot *p, *q; /* for scanning the path */
4357 scaled d; /* a temporary value */
4358 boolean curved; /* |true| unless the cubic is almost straight */
4360 mp_ps_print_cmd(mp, "newpath ","n ");
4361 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4362 mp_ps_print_cmd(mp, "moveto","m");
4365 if ( gr_right_type(p)==mp_endpoint ) {
4366 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4370 @<Start a new line and print the \ps\ commands for the curve from
4374 mp_ps_print_cmd(mp, " closepath"," p");
4377 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4379 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4382 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4383 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4384 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4385 mp_ps_print_cmd(mp, "curveto","c");
4387 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4388 mp_ps_print_cmd(mp, "lineto","l");
4391 @ Two types of straight lines come up often in \MP\ paths:
4392 cubics with zero initial and final velocity as created by |make_path| or
4393 |make_envelope|, and cubics with control points uniformly spaced on a line
4394 as created by |make_choices|.
4396 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4398 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4399 if ( gr_right_x(p)==gr_x_coord(p) )
4400 if ( gr_right_y(p)==gr_y_coord(p) )
4401 if ( gr_left_x(q)==gr_x_coord(q) )
4402 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4403 d=gr_left_x(q)-gr_right_x(p);
4404 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4405 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4406 { d=gr_left_y(q)-gr_right_y(p);
4407 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4408 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4411 @ The colored objects use a struct with anonymous fields to express the color parts:
4415 int a_val, b_val, c_val, d_val;
4418 @ The exported form of a dash pattern is simpler than the internal
4419 format, it is closely modelled to the PostScript model. The array of
4420 dashes is ended by a single negative value, because this is not
4421 allowed in PostScript.
4431 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
4434 static void mp_do_gr_toss_dashes(mp_dash_object *dl);
4437 void mp_do_gr_toss_dashes(mp_dash_object *dl) {
4440 mp_xfree(dl->array);
4446 static mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) {
4447 mp_dash_object *q = NULL;
4451 q = mp_xmalloc(mp, 1, sizeof (mp_dash_object));
4452 memcpy (q,dl,sizeof(mp_dash_object));
4453 if (dl->array != NULL) {
4455 while (*(dl->array+i) != -1) i++;
4456 q->array = mp_xmalloc(mp, i, sizeof (scaled));
4457 memcpy(q->array,dl->array, (i*sizeof(scaled)));
4463 @ Now for outputting the actual graphic objects. First, set up some
4464 structures and access macros.
4466 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4469 #define gr_type(A) (A)->type
4470 #define gr_link(A) (A)->next
4471 #define gr_color_model(A) (A)->color_model
4472 #define gr_red_val(A) (A)->color.a_val
4473 #define gr_green_val(A) (A)->color.b_val
4474 #define gr_blue_val(A) (A)->color.c_val
4475 #define gr_cyan_val(A) (A)->color.a_val
4476 #define gr_magenta_val(A) (A)->color.b_val
4477 #define gr_yellow_val(A) (A)->color.c_val
4478 #define gr_black_val(A) (A)->color.d_val
4479 #define gr_grey_val(A) (A)->color.a_val
4480 #define gr_path_p(A) (A)->path_p
4481 #define gr_htap_p(A) ((mp_fill_object *)A)->htap_p
4482 #define gr_pen_p(A) (A)->pen_p
4483 #define gr_ljoin_val(A) (A)->ljoin
4484 #define gr_lcap_val(A) ((mp_stroked_object *)A)->lcap
4485 #define gr_miterlim_val(A) (A)->miterlim
4486 #define gr_pre_script(A) (A)->pre_script
4487 #define gr_post_script(A) (A)->post_script
4488 #define gr_dash_p(A) ((mp_stroked_object *)A)->dash_p
4489 #define gr_size_index(A) ((mp_text_object *)A)->size_index
4490 #define gr_text_p(A) ((mp_text_object *)A)->text_p
4491 #define gr_font_n(A) ((mp_text_object *)A)->font_n
4492 #define gr_font_name(A) ((mp_text_object *)A)->font_name
4493 #define gr_font_dsize(A) ((mp_text_object *)A)->font_dsize
4494 #define gr_width_val(A) ((mp_text_object *)A)->width
4495 #define gr_height_val(A) ((mp_text_object *)A)->height
4496 #define gr_depth_val(A) ((mp_text_object *)A)->depth
4497 #define gr_tx_val(A) ((mp_text_object *)A)->tx
4498 #define gr_ty_val(A) ((mp_text_object *)A)->ty
4499 #define gr_txx_val(A) ((mp_text_object *)A)->txx
4500 #define gr_txy_val(A) ((mp_text_object *)A)->txy
4501 #define gr_tyx_val(A) ((mp_text_object *)A)->tyx
4502 #define gr_tyy_val(A) ((mp_text_object *)A)->tyy
4505 #define GRAPHIC_BODY \
4507 struct mp_graphic_object * next
4509 typedef struct mp_graphic_object {
4511 } mp_graphic_object;
4513 typedef struct mp_text_object {
4518 unsigned char color_model;
4519 unsigned char size_index;
4522 unsigned int font_dsize ;
4523 unsigned int font_n ;
4535 typedef struct mp_fill_object {
4540 unsigned char color_model;
4541 unsigned char ljoin ;
4548 typedef struct mp_stroked_object {
4553 unsigned char color_model;
4554 unsigned char ljoin ;
4555 unsigned char lcap ;
4559 mp_dash_object *dash_p;
4560 } mp_stroked_object;
4562 typedef struct mp_clip_object {
4567 typedef struct mp_bounds_object {
4572 typedef struct mp_special_object {
4575 } mp_special_object ;
4577 typedef struct mp_edge_object {
4578 struct mp_graphic_object * body;
4579 struct mp_edge_object * next;
4582 int minx, miny, maxx, maxy;
4583 int width, height, depth, ital_corr;
4587 @ @<Exported function headers@>=
4588 mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4591 mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4592 mp_graphic_object *p;
4595 case mp_fill_code: size = sizeof(mp_fill_object); break;
4596 case mp_stroked_code: size = sizeof(mp_stroked_object); break;
4597 case mp_text_code: size = sizeof(mp_text_object); break;
4598 case mp_start_clip_code: size = sizeof(mp_clip_object); break;
4599 case mp_start_bounds_code: size = sizeof(mp_bounds_object); break;
4600 case mp_special_code: size = sizeof(mp_special_object); break;
4601 default: size = sizeof(mp_graphic_object); break;
4603 p = (mp_graphic_object *)mp_xmalloc(mp,1,size);
4609 @ We need to keep track of several parameters from the \ps\ graphics state.
4611 This allows us to be sure that \ps\ has the correct values when they are
4612 needed without wasting time and space setting them unnecessarily.
4614 @d gs_red mp->ps->gs_state->red_field
4615 @d gs_green mp->ps->gs_state->green_field
4616 @d gs_blue mp->ps->gs_state->blue_field
4617 @d gs_black mp->ps->gs_state->black_field
4618 @d gs_colormodel mp->ps->gs_state->colormodel_field
4619 @d gs_ljoin mp->ps->gs_state->ljoin_field
4620 @d gs_lcap mp->ps->gs_state->lcap_field
4621 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4622 @d gs_miterlim mp->ps->gs_state->miterlim_field
4623 @d gs_dash_p mp->ps->gs_state->dash_p_field
4624 @d gs_dash_init_done mp->ps->gs_state->dash_done_field
4625 @d gs_previous mp->ps->gs_state->previous_field
4626 @d gs_width mp->ps->gs_state->width_field
4629 typedef struct _gs_state {
4631 scaled green_field ;
4633 scaled black_field ;
4634 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4635 quarterword colormodel_field ;
4636 /* the current colormodel */
4637 quarterword ljoin_field ;
4638 quarterword lcap_field ;
4639 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4640 quarterword adj_wx_field ;
4641 /* what resolution-dependent adjustment applies to the width */
4642 scaled miterlim_field ;
4643 /* the value from the last \&{setmiterlimit} command */
4644 mp_dash_object * dash_p_field ;
4645 /* edge structure for last \&{setdash} command */
4646 boolean dash_done_field ; /* to test for initial \&{setdash} */
4647 struct _gs_state * previous_field ;
4648 /* backlink to the previous |_gs_state| structure */
4649 scaled width_field ;
4650 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4655 struct _gs_state * gs_state;
4658 mp->ps->gs_state=NULL;
4660 @ @<Dealloc variables@>=
4661 mp_xfree(mp->ps->gs_state);
4663 @ To avoid making undue assumptions about the initial graphics state, these
4664 parameters are given special values that are guaranteed not to match anything
4665 in the edge structure being shipped out. On the other hand, the initial color
4666 should be black so that the translation of an all-black picture will have no
4667 \&{setcolor} commands. (These would be undesirable in a font application.)
4668 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4669 to recover from a situation where we have lost track of the graphics state.
4671 @d mp_void (null+1) /* a null pointer different from |null| */
4673 @c static void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4674 struct _gs_state *p; /* to shift graphic states around */
4675 if ( (c==0)||(c==-1) ) {
4676 if ( mp->ps->gs_state==NULL ) {
4677 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4680 while ( gs_previous!=NULL ) {
4682 mp_xfree(mp->ps->gs_state);
4686 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4687 gs_colormodel=mp_uninitialized_model;
4692 gs_dash_init_done=false;
4694 } else if ( c==1 ) {
4695 p= mp->ps->gs_state;
4696 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4697 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4699 } else if ( c==2 ) {
4701 mp_xfree(mp->ps->gs_state);
4707 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4708 that \ps's idea of the graphics state agrees with what is stored in the object.
4711 static void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ;
4714 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) {
4715 /* get ready to output graphical object |p| */
4716 mp_knot *pp, *path_p; /* for list manipulation */
4718 scaled wx,wy,ww; /* dimensions of pen bounding box */
4719 quarterword adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4720 integer tx,ty; /* temporaries for computing |adj_wx| */
4721 if ( gr_has_color(p) )
4722 @<Make sure \ps\ will use the right color for object~|p|@>;
4723 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) {
4724 if (gr_type(p)==mp_fill_code) {
4725 pp = gr_pen_p((mp_fill_object *)p);
4726 path_p = gr_path_p((mp_fill_object *)p);
4728 pp = gr_pen_p((mp_stroked_object *)p);
4729 path_p = gr_path_p((mp_stroked_object *)p);
4732 if ( pen_is_elliptical(pp) ) {
4733 @<Generate \ps\ code that sets the stroke width to the
4734 appropriate rounded value@>;
4735 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4736 @<Decide whether the line cap parameter matters and set it if necessary@>;
4737 @<Set the other numeric parameters as needed for object~|p|@>;
4740 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
4743 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4744 if ( gr_type(p)==mp_stroked_code ) {
4745 mp_stroked_object *ts = (mp_stroked_object *)p;
4746 if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) )
4747 if ( gs_lcap!=(quarterword)gr_lcap_val(ts) ) {
4749 mp_ps_print_char(mp, ' ');
4750 mp_ps_print_char(mp, '0'+gr_lcap_val(ts));
4751 mp_ps_print_cmd(mp, " setlinecap"," lc");
4752 gs_lcap=(quarterword)gr_lcap_val(ts);
4757 @d set_ljoin_miterlim(p)
4758 if ( gs_ljoin!=(quarterword)gr_ljoin_val(p) ) {
4760 mp_ps_print_char(mp, ' ');
4761 mp_ps_print_char(mp, '0'+gr_ljoin_val(p));
4762 mp_ps_print_cmd(mp, " setlinejoin"," lj");
4763 gs_ljoin=(quarterword)gr_ljoin_val(p);
4765 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4767 mp_ps_print_char(mp, ' ');
4768 mp_ps_print_scaled(mp, gr_miterlim_val(p));
4769 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4770 gs_miterlim=gr_miterlim_val(p);
4773 @<Set the other numeric parameters as needed for object~|p|@>=
4774 if ( gr_type(p)==mp_stroked_code ) {
4775 mp_stroked_object *ts = (mp_stroked_object *)p;
4776 set_ljoin_miterlim(ts);
4778 mp_fill_object *ts = (mp_fill_object *)p;
4779 set_ljoin_miterlim(ts);
4783 @d set_color_objects(pq)
4784 object_color_model = pq->color_model;
4785 object_color_a = pq->color.a_val;
4786 object_color_b = pq->color.b_val;
4787 object_color_c = pq->color.c_val;
4788 object_color_d = pq->color.d_val;
4790 @<Make sure \ps\ will use the right color for object~|p|@>=
4792 int object_color_model;
4793 int object_color_a, object_color_b, object_color_c, object_color_d ;
4794 if (gr_type(p) == mp_fill_code) {
4795 mp_fill_object *pq = (mp_fill_object *)p;
4796 set_color_objects(pq);
4797 } else if (gr_type(p) == mp_stroked_code) {
4798 mp_stroked_object *pq = (mp_stroked_object *)p;
4799 set_color_objects(pq);
4801 mp_text_object *pq = (mp_text_object *)p;
4802 set_color_objects(pq);
4805 if ( object_color_model==mp_rgb_model) {
4806 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)||
4807 (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) {
4808 gs_red = object_color_a;
4809 gs_green = object_color_b;
4810 gs_blue = object_color_c;
4812 gs_colormodel=mp_rgb_model;
4814 mp_ps_print_char(mp, ' ');
4815 mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
4816 mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
4817 mp_ps_print_scaled(mp, gs_blue);
4818 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4821 } else if ( object_color_model==mp_cmyk_model) {
4822 if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)||
4823 (gs_blue!=object_color_c)||(gs_black!=object_color_d)||
4824 (gs_colormodel!=mp_cmyk_model) ) {
4825 gs_red = object_color_a;
4826 gs_green = object_color_b;
4827 gs_blue = object_color_c;
4828 gs_black = object_color_d;
4829 gs_colormodel=mp_cmyk_model;
4831 mp_ps_print_char(mp, ' ');
4832 mp_ps_print_scaled(mp, gs_red);
4833 mp_ps_print_char(mp, ' ');
4834 mp_ps_print_scaled(mp, gs_green);
4835 mp_ps_print_char(mp, ' ');
4836 mp_ps_print_scaled(mp, gs_blue);
4837 mp_ps_print_char(mp, ' ');
4838 mp_ps_print_scaled(mp, gs_black);
4839 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4842 } else if ( object_color_model==mp_grey_model ) {
4843 if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) {
4844 gs_red = object_color_a;
4848 gs_colormodel=mp_grey_model;
4850 mp_ps_print_char(mp, ' ');
4851 mp_ps_print_scaled(mp, gs_red);
4852 mp_ps_print_cmd(mp, " setgray"," G");
4855 } else if ( object_color_model==mp_no_model ) {
4856 gs_colormodel=mp_no_model;
4860 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4861 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4862 @:setwidth}{\&{setwidth}command@>
4863 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4864 generate \ps\ code that computes the rounded value. For non-circular pens, the
4865 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4866 have the computed width.
4868 Rounding the width to whole pixels is not likely to improve the appearance of
4869 diagonal or curved strokes, but we do it anyway for consistency. The
4870 \&{truncate} command generated here tends to make all the strokes a little
4871 @:truncate}{\&{truncate} command@>
4872 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4873 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4874 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4877 @<Generate \ps\ code that sets the stroke width...@>=
4878 @<Set |wx| and |wy| to the width and height of the bounding box for
4880 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4881 important and set |adj_wx| and |ww| accordingly@>;
4882 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4883 if ( adj_wx != 0 ) {
4885 mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
4887 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4889 if ( mp->internal[mp_procset]>0 ) {
4891 mp_ps_print_char(mp, ' ');
4892 mp_ps_print_scaled(mp, ww);
4893 mp_ps_print(mp, " vlw");
4896 mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
4897 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4904 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4905 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4906 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4907 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4909 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4910 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4913 @ The path is considered ``essentially horizontal'' if its range of
4914 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4915 vertical'' paths are detected similarly. This code ensures that no component
4916 of the pen transformation is more that |aspect_bound*(ww+1)|.
4918 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4919 more than this factor */
4924 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4926 if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound;
4927 else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound;
4928 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=0; }
4929 else { ww=wx; adj_wx=1; }
4931 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4932 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4933 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4934 bounding-box computation.
4937 static boolean mp_gr_coord_rangeOK (mp_knot *h,
4938 quarterword zoff, scaled dz);
4941 boolean mp_gr_coord_rangeOK (mp_knot *h,
4942 quarterword zoff, scaled dz) {
4943 mp_knot *p; /* for scanning the path form |h| */
4944 scaled zlo,zhi; /* coordinate range so far */
4945 scaled z; /* coordinate currently being tested */
4946 if (zoff==do_x_loc) {
4950 while ( gr_right_type(p)!=mp_endpoint ) {
4952 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4953 p=gr_next_knot(p); z=gr_left_x(p);
4954 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4956 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4963 while ( gr_right_type(p)!=mp_endpoint ) {
4965 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4966 p=gr_next_knot(p); z=gr_left_y(p);
4967 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4969 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4976 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4978 else if ( z>zhi ) zhi=z;
4979 if ( zhi-zlo>dz ) return false
4981 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4982 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4983 @:stroke}{\&{stroke} command@>
4984 @:fill}{\&{fill} command@>
4985 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4988 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4989 if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) {
4995 if ( gs_dash_p!=NULL || gs_dash_init_done == false) {
4996 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4998 gs_dash_init_done=true;
5000 } else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) {
5001 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
5004 @ The original code had a check here to ensure that the result from
5005 |mp_take_scaled| did not go out of bounds.
5007 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5009 if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array==NULL)) {
5010 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5014 mp_ps_print(mp, " [");
5015 for (i=0; *(hh->array+i) != -1;i++) {
5017 mp_ps_print_scaled(mp, *(hh->array+i));
5018 mp_ps_print_char(mp, ' ') ;
5021 mp_ps_print(mp, "] ");
5022 mp_ps_print_scaled(mp, hh->offset);
5023 mp_ps_print_cmd(mp, " setdash"," sd");
5028 static boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ;
5030 @ This function test if |h| and |hh| represent the same dash pattern.
5033 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) {
5036 if ( h==hh ) ret=true;
5037 else if ( (h==NULL)||(hh==NULL) ) ret=false;
5038 else if ( h->offset!=hh->offset ) ret=false;
5039 else if ( h->array == hh->array ) ret=true;
5040 else if ( h->array == NULL || hh->array == NULL) ret=false;
5041 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5045 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5047 while (*(h->array+i)!=-1 &&
5048 *(hh->array+i)!=-1 &&
5049 *(h->array+i) == *(hh->array+i)) i++;
5051 if (*(h->array+i)==-1 && *(hh->array+i) == -1)
5056 @ When stroking a path with an elliptical pen, it is necessary to transform
5057 the coordinate system so that a unit circular pen will have the desired shape.
5058 To keep this transformation local, we enclose it in a
5059 $$\&{gsave}\ldots\&{grestore}$$
5060 block. Any translation component must be applied to the path being stroked
5061 while the rest of the transformation must apply only to the pen.
5062 If |fill_also=true|, the path is to be filled as well as stroked so we must
5063 insert commands to do this after giving the path.
5066 static void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) ;
5069 @c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) {
5070 /* generate an elliptical pen stroke from object |h| */
5071 scaled txx,txy,tyx,tyy; /* transformation parameters */
5072 mp_knot *p; /* the pen to stroke with */
5073 scaled d1,det; /* for tweaking transformation parameters */
5074 integer s; /* also for tweaking transformation paramters */
5075 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5077 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5079 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5080 if (gr_type(h)==mp_fill_code) {
5081 mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h));
5083 mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h));
5085 if ( mp->internal[mp_procset]==0 ) {
5086 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5087 @<Issue \ps\ commands to transform the coordinate system@>;
5088 mp_ps_print(mp, " stroke");
5089 if ( transformed ) mp_ps_print(mp, " grestore");
5091 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5092 if ( (txy!=0)||(tyx!=0) ) {
5093 mp_ps_print(mp, " [");
5094 mp_ps_pair_out(mp, txx,tyx);
5095 mp_ps_pair_out(mp, txy,tyy);
5096 mp_ps_print(mp, "0 0] t");
5097 } else if ((txx!=unity)||(tyy!=unity) ) {
5098 mp_ps_pair_out(mp,txx,tyy);
5099 mp_ps_print(mp, " s");
5101 mp_ps_print(mp, " S");
5102 if ( transformed ) mp_ps_print(mp, " Q");
5107 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5108 if (gr_type(h)==mp_fill_code) {
5109 p=gr_pen_p((mp_fill_object *)h);
5111 p=gr_pen_p((mp_stroked_object *)h);
5117 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5118 mp_ps_print_nl(mp, "");
5119 mp_ps_print_cmd(mp, "gsave ","q ");
5120 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5121 mp_ps_print(mp, "translate ");
5128 mp_ps_print_nl(mp, "");
5130 @<Adjust the transformation to account for |gs_width| and output the
5131 initial \&{gsave} if |transformed| should be |true|@>
5133 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5134 if ( gs_width!=unity ) {
5135 if ( gs_width==0 ) {
5136 txx=unity; tyy=unity;
5138 txx=mp_make_scaled(mp, txx,gs_width);
5139 txy=mp_make_scaled(mp, txy,gs_width);
5140 tyx=mp_make_scaled(mp, tyx,gs_width);
5141 tyy=mp_make_scaled(mp, tyy,gs_width);
5144 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5145 if ( (! transformed) ){
5146 mp_ps_print_cmd(mp, "gsave ","q ");
5151 @ @<Issue \ps\ commands to transform the coordinate system@>=
5152 if ( (txy!=0)||(tyx!=0) ){
5154 mp_ps_print_char(mp, '[');
5155 mp_ps_pair_out(mp, txx,tyx);
5156 mp_ps_pair_out(mp, txy,tyy);
5157 mp_ps_print(mp, "0 0] concat");
5158 } else if ( (txx!=unity)||(tyy!=unity) ){
5160 mp_ps_pair_out(mp, txx,tyy);
5161 mp_ps_print(mp, "scale");
5164 @ The \ps\ interpreter will probably abort if it encounters a singular
5165 transformation matrix. The determinant must be large enough to ensure that
5166 the printed representation will be nonsingular. Since the printed
5167 representation is always within $2^{-17}$ of the internal |scaled| value, the
5168 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5169 the magnitudes of |txx/65536|, |txy/65536|, etc.
5171 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5172 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5174 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5175 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5176 d1=4*aspect_bound+1;
5177 if ( abs(det)<d1 ) {
5178 if ( det>=0 ) { d1=d1-det; s=1; }
5179 else { d1=-d1-det; s=-1; };
5181 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5182 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5183 else txx=txx+(d1+s*abs(tyy)) / tyy;
5185 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5186 else txy=txy+(d1+s*abs(tyx)) / tyx;
5190 @ Here is a simple routine that just fills a cycle.
5193 static void mp_gr_ps_fill_out (MP mp, mp_knot *p);
5196 void mp_gr_ps_fill_out (MP mp, mp_knot *p) { /* fill cyclic path~|p| */
5197 mp_gr_ps_path_out(mp, p);
5198 mp_ps_print_cmd(mp, " fill"," F");
5202 @ A text node may specify an arbitrary transformation but the usual case
5203 involves only shifting, scaling, and occasionally rotation. The purpose
5204 of |choose_scale| is to select a scale factor so that the remaining
5205 transformation is as ``nice'' as possible. The definition of ``nice''
5206 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5207 nice because they work out well for bitmap fonts. The code here selects
5208 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5209 non-shifting part of the transformation matrix. It is careful to avoid
5210 additions that might cause undetected overflow.
5213 static scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) ;
5215 @ @c scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) {
5216 /* |p| should point to a text node */
5217 scaled a,b,c,d,ad,bc; /* temporary values */
5222 if ( a<0 ) negate(a);
5223 if ( b<0 ) negate(b);
5224 if ( c<0 ) negate(c);
5225 if ( d<0 ) negate(d);
5228 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5231 @ The potential overflow here is caused by the fact the returned value
5232 has to fit in a |name_type|, which is a quarterword.
5234 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
5237 static quarterword mp_size_index (MP mp, font_number f, scaled s) ;
5240 quarterword mp_size_index (MP mp, font_number f, scaled s) {
5241 pointer p,q; /* the previous and current font size nodes */
5242 quarterword i; /* the size index for |q| */
5243 q=mp->font_sizes[f];
5246 if ( abs(s-sc_factor(q))<=fscale_tolerance )
5249 { p=q; q=link(q); incr(i); };
5250 if ( i==max_quarterword )
5251 mp_overflow(mp, "sizes per font",max_quarterword);
5252 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
5254 q=mp_get_node(mp, font_size_size);
5256 if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
5261 static scaled mp_indexed_size (MP mp,font_number f, quarterword j);
5264 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
5265 pointer p; /* a font size node */
5266 quarterword i; /* the size index for |p| */
5267 p=mp->font_sizes[f];
5269 if ( p==null ) mp_confusion(mp, "size");
5272 if ( p==null ) mp_confusion(mp, "size");
5274 return sc_factor(p);
5278 static void mp_clear_sizes (MP mp) ;
5280 @ @c void mp_clear_sizes (MP mp) {
5281 font_number f; /* the font whose size list is being cleared */
5282 pointer p; /* current font size nodes */
5283 for (f=null_font+1;f<=mp->last_fnum;f++) {
5284 while ( mp->font_sizes[f]!=null ) {
5285 p=mp->font_sizes[f];
5286 mp->font_sizes[f]=link(p);
5287 mp_free_node(mp, p,font_size_size);
5292 @ A text node may specify an arbitrary transformation but the usual case
5293 involves only shifting, scaling, and occasionally rotation. The purpose
5294 of |choose_scale| is to select a scale factor so that the remaining
5295 transformation is as ``nice'' as possible. The definition of ``nice''
5296 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5297 nice because they work out well for bitmap fonts. The code here selects
5298 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5299 non-shifting part of the transformation matrix. It is careful to avoid
5300 additions that might cause undetected overflow.
5303 @ @c static scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
5304 /* |p| should point to a text node */
5305 scaled a,b,c,d,ad,bc; /* temporary values */
5310 if ( (a<0) ) negate(a);
5311 if ( (b<0) ) negate(b);
5312 if ( (c<0) ) negate(c);
5313 if ( (d<0) ) negate(d);
5316 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5319 @ There may be many sizes of one font and we need to keep track of the
5320 characters used for each size. This is done by keeping a linked list of
5321 sizes for each font with a counter in each text node giving the appropriate
5322 position in the size list for its font.
5324 @d font_size_size 2 /* size of a font size node */
5328 static void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size);
5331 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) {
5332 mp_graphic_object * p;
5334 while ( p!= NULL ) {
5335 if ( gr_type(p)==mp_text_code ) {
5336 if ( gr_font_n(p)!=null_font ) {
5337 if ( gr_size_index(p)==(unsigned char)next_size )
5338 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p));
5345 @ @<Unmark all marked characters@>=
5346 for (f=null_font+1;f<=mp->last_fnum;f++) {
5347 if ( mp->font_sizes[f]!=null ) {
5348 mp_unmark_font(mp, f);
5349 mp->font_sizes[f]=null;
5353 @ @<Scan all the text nodes and mark the used ...@>=
5356 if ( gr_type(p)==mp_text_code ) {
5358 if (f!=null_font ) {
5359 switch (prologues) {
5362 mp->font_sizes[f] = mp_void;
5363 mp_mark_string_chars(mp, f, gr_text_p(p));
5364 if (mp_has_fm_entry(mp,f,NULL) ) {
5365 if (mp->font_enc_name[f]==NULL )
5366 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
5367 mp->font_ps_name[f] = mp_fm_font_name(mp,f);
5371 mp->font_sizes[f]=mp_void;
5374 gr_size_index(p)=(unsigned char)mp_size_index(mp, f,mp_choose_scale(mp, p));
5375 if ( gr_size_index(p)==0 )
5376 mp_mark_string_chars(mp, f, gr_text_p(p));
5385 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5387 @<Exported function ...@>=
5388 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) ;
5391 int mp_gr_ship_out (mp_edge_object *hh, int qprologues, int qprocset,int standalone) {
5392 mp_graphic_object *p;
5393 scaled ds,scf; /* design size and scale factor for a text node */
5394 font_number f; /* for loops over fonts while (un)marking characters */
5395 boolean transformed; /* is the coordinate system being transformed? */
5396 int prologues, procset;
5399 mp->jump_buf = malloc(sizeof(jmp_buf));
5400 if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)))
5403 if (mp->history >= mp_fatal_error_stop ) return 1;
5405 prologues = (int)((unsigned)mp->internal[mp_prologues]>>16);
5407 prologues=qprologues;
5409 procset = (int)((unsigned)mp->internal[mp_procset]>>16);
5412 mp_open_output_file(mp);
5413 mp_print_initial_comment(mp, hh, prologues);
5415 @<Unmark all marked characters@>;
5416 if ( prologues==2 || prologues==3 ) {
5417 mp_reload_encodings(mp);
5419 @<Scan all the text nodes and mark the used characters@>;
5420 if ( prologues==2 || prologues==3 ) {
5421 mp_print_improved_prologue(mp, hh, prologues, procset);
5423 mp_print_prologue(mp, hh, prologues, procset);
5425 mp_gs_unknown_graphics_state(mp, 0);
5428 if ( gr_has_color(p) ) {
5429 @<Write |pre_script| of |p|@>;
5431 mp_gr_fix_graphics_state(mp, p);
5432 switch (gr_type(p)) {
5434 if ( gr_pen_p((mp_fill_object *)p)==NULL ) {
5435 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5436 } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) ) {
5437 mp_gr_stroke_ellipse(mp, p,true);
5439 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5440 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5442 if ( gr_post_script((mp_fill_object *)p)!=NULL ) {
5443 mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p));
5447 case mp_stroked_code:
5448 if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) )
5449 mp_gr_stroke_ellipse(mp, p,false);
5451 mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p));
5453 if ( gr_post_script((mp_stroked_object *)p)!=NULL ) {
5454 mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p));
5459 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5461 scf=mp_gr_choose_scale(mp, p);
5463 scf=mp_indexed_size(mp, gr_font_n(p), (quarterword)gr_size_index(p));
5464 @<Shift or transform as necessary before outputting text node~|p| at scale
5465 factor~|scf|; set |transformed:=true| if the original transformation must
5467 mp_ps_string_out(mp, gr_text_p(p));
5468 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5469 @<Print the size information and \ps\ commands for text node~|p|@>;
5472 if ( gr_post_script((mp_text_object *)p)!=NULL ) {
5473 mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp);
5476 case mp_start_clip_code:
5477 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5478 mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p));
5479 mp_ps_print_cmd(mp, " clip"," W");
5481 if ( mp->internal[mp_restore_clip_color]>0 )
5482 mp_gs_unknown_graphics_state(mp, 1);
5484 case mp_stop_clip_code:
5485 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5487 if ( mp->internal[mp_restore_clip_color]>0 )
5488 mp_gs_unknown_graphics_state(mp, 2);
5490 mp_gs_unknown_graphics_state(mp, -1);
5492 case mp_start_bounds_code:
5493 case mp_stop_bounds_code:
5495 case mp_special_code:
5497 mp_special_object *ps = (mp_special_object *)p;
5498 mp_ps_print_nl (mp, gr_pre_script(ps));
5499 mp_ps_print_ln (mp);
5502 } /* all cases are enumerated */
5505 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
5506 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
5507 (mp->close_file)(mp,mp->ps_file);
5514 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) ;
5517 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) {
5518 return mp_gr_ship_out (hh, prologues, procset, (int)true);
5526 @d do_write_prescript(a,b) {
5527 if ( (gr_pre_script((b *)a))!=NULL ) {
5528 mp_ps_print_nl (mp, gr_pre_script((b *)a));
5533 @<Write |pre_script| of |p|@>=
5535 if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); }
5536 else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); }
5537 else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); }
5540 @ The envelope of a cyclic path~|q| could be computed by calling
5541 |make_envelope| once for |q| and once for its reversal. We don't do this
5542 because it would fail color regions that are covered by the pen regardless
5543 of where it is placed on~|q|.
5545 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5546 if ( gr_left_type(q)!=mp_endpoint ) {
5547 gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5548 gr_right_type(q)=mp_endpoint;
5553 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5555 mp_ps_print_char(mp, ' ');
5556 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5557 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5558 mp_ps_print(mp, " fshow");
5560 mp_ps_print_cmd(mp, " grestore"," Q")
5564 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5565 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5566 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5567 if ( transformed ) {
5568 mp_ps_print_cmd(mp, "gsave [", "q [");
5569 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5570 mp_make_scaled(mp, gr_tyx_val(p),scf));
5571 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5572 mp_make_scaled(mp, gr_tyy_val(p),scf));
5573 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5574 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5576 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5577 mp_ps_print_cmd(mp, "moveto","m");
5582 void mp_gr_toss_objects ( mp_edge_object *hh) ;
5583 void mp_gr_toss_object (mp_graphic_object *p) ;
5586 void mp_gr_toss_object (mp_graphic_object *p) {
5588 mp_stroked_object *ts;
5590 switch (gr_type(p)) {
5592 tf = (mp_fill_object *)p;
5593 mp_xfree(gr_pre_script(tf));
5594 mp_xfree(gr_post_script(tf));
5595 mp_gr_toss_knot_list(mp,gr_pen_p(tf));
5596 mp_gr_toss_knot_list(mp,gr_path_p(tf));
5597 mp_gr_toss_knot_list(mp,gr_htap_p(p));
5599 case mp_stroked_code:
5600 ts = (mp_stroked_object *)p;
5601 mp_xfree(gr_pre_script(ts));
5602 mp_xfree(gr_post_script(ts));
5603 mp_gr_toss_knot_list(mp,gr_pen_p(ts));
5604 mp_gr_toss_knot_list(mp,gr_path_p(ts));
5605 if (gr_dash_p(p)!=NULL)
5606 mp_gr_toss_dashes (mp,gr_dash_p(p));
5609 tt = (mp_text_object *)p;
5610 mp_xfree(gr_pre_script(tt));
5611 mp_xfree(gr_post_script(tt));
5612 mp_xfree(gr_text_p(p));
5613 mp_xfree(gr_font_name(p));
5615 case mp_start_clip_code:
5616 mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p));
5618 case mp_start_bounds_code:
5619 mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p));
5621 case mp_stop_clip_code:
5622 case mp_stop_bounds_code:
5624 case mp_special_code:
5625 mp_xfree(gr_pre_script((mp_special_object *)p));
5627 } /* all cases are enumerated */
5633 void mp_gr_toss_objects (mp_edge_object *hh) {
5634 mp_graphic_object *p, *q;
5638 mp_gr_toss_object(p);
5645 mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ;
5649 mp_gr_copy_object (MP mp, mp_graphic_object *p) {
5651 mp_stroked_object *ts;
5654 mp_bounds_object *tb;
5655 mp_special_object *tp;
5656 mp_graphic_object *q = NULL;
5657 switch (gr_type(p)) {
5659 tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code);
5660 gr_pre_script(tf) = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p));
5661 gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p));
5662 gr_path_p(tf) = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p));
5663 gr_htap_p(tf) = mp_gr_copy_path(mp,gr_htap_p(p));
5664 gr_pen_p(tf) = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p));
5665 q = (mp_graphic_object *)tf;
5667 case mp_stroked_code:
5668 ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code);
5669 gr_pre_script(ts) = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p));
5670 gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p));
5671 gr_path_p(ts) = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p));
5672 gr_pen_p(ts) = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p));
5673 gr_dash_p(ts) = mp_gr_copy_dashes(mp,gr_dash_p(p));
5674 q = (mp_graphic_object *)ts;
5677 tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code);
5678 gr_pre_script(tt) = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p));
5679 gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p));
5680 gr_text_p(tt) = mp_xstrdup(mp, gr_text_p(p));
5681 gr_font_name(tt) = mp_xstrdup(mp, gr_font_name(p));
5682 q = (mp_graphic_object *)tt;
5684 case mp_start_clip_code:
5685 tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code);
5686 gr_path_p(tc) = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p));
5687 q = (mp_graphic_object *)tc;
5689 case mp_start_bounds_code:
5690 tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code);
5691 gr_path_p(tb) = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p));
5692 q = (mp_graphic_object *)tb;
5694 case mp_special_code:
5695 tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code);
5696 gr_pre_script(tp) = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p));
5697 q = (mp_graphic_object *)tp;
5699 case mp_stop_clip_code:
5700 q = mp_new_graphic_object(mp, mp_stop_clip_code);
5702 case mp_stop_bounds_code:
5703 q = mp_new_graphic_object(mp, mp_stop_bounds_code);
5705 } /* all cases are enumerated */