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@>
101 @ @<Exported function headers@>=
102 void mp_backend_initialize (MP mp) ;
103 void mp_backend_free (MP mp) ;
106 @c void mp_backend_initialize (MP mp) {
107 mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
108 @<Set initial values@>;
110 void mp_backend_free (MP mp) {
111 @<Dealloc variables@>;
119 @ Writing to ps files
123 /* the number of characters on the current \ps\ file line */
125 @ @<Set initial values@>=
126 mp->ps->ps_offset = 0;
130 @d wps(A) (mp->write_ascii_file)(mp,mp->ps_file,(A))
134 (mp->write_ascii_file)(mp,mp->ps_file,(char *)ss);
136 @d wps_cr (mp->write_ascii_file)(mp,mp->ps_file,"\n")
137 @d wps_ln(A) { wterm_cr; (mp->write_ascii_file)(mp,mp->ps_file,(A)); }
140 void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
146 void mp_ps_print_char (MP mp, ASCII_code s) { /* prints a single character */
148 wps_cr; mp->ps->ps_offset=0;
150 wps_chr(s); incr(mp->ps->ps_offset);
155 void mp_ps_do_print (MP mp, const char *ss, unsigned int len) { /* prints string |s| */
158 mp_ps_print_char(mp, ss[j]); incr(j);
162 @ Deciding where to break the ps output line.
164 @d ps_room(A) if ( (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) {
165 mp_ps_print_ln(mp); /* optional line break */
169 void mp_ps_print (MP mp, const char *ss) {
171 mp_ps_do_print(mp, ss, strlen(ss));
174 @ The procedure |print_nl| is like |print|, but it makes sure that the
175 string appears at the beginning of a new line.
178 void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */
179 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
183 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
186 void mp_ps_print_the_digs (MP mp, eight_bits k) {
187 /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
189 mp_ps_print_char(mp, '0'+mp->dig[k]);
193 @ The following procedure, which prints out the decimal representation of a
194 given integer |n|, has been written carefully so that it works properly
195 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
196 to negative arguments, since such operations are not implemented consistently
197 by all \PASCAL\ compilers.
200 void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */
201 integer m; /* used to negate |n| in possibly dangerous cases */
202 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
204 mp_ps_print_char(mp, '-');
205 if ( n>-100000000 ) {
208 m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
212 mp->dig[0]=0; incr(n);
217 mp->dig[k]=n % 10; n=n / 10; incr(k);
219 mp_ps_print_the_digs(mp, k);
223 void mp_ps_print_int (MP mp,integer n);
225 @ \MP\ also makes use of a trivial procedure to print two digits. The
226 following subroutine is usually called with a parameter in the range |0<=n<=99|.
229 void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */
231 mp_ps_print_char(mp, '0'+(n / 10));
232 mp_ps_print_char(mp, '0'+(n % 10));
235 @ Conversely, here is a procedure analogous to |print_int|. If the output
236 of this procedure is subsequently read by \MP\ and converted by the
237 |round_decimals| routine above, it turns out that the original value will
238 be reproduced exactly. A decimal point is printed only if the value is
239 not an integer. If there is more than one way to print the result with
240 the optimum number of digits following the decimal point, the closest
241 possible value is given.
243 The invariant relation in the \&{repeat} loop is that a sequence of
244 decimal digits yet to be printed will yield the original number if and only if
245 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
246 We can stop if and only if $f=0$ satisfies this condition; the loop will
247 terminate before $s$ can possibly become zero.
250 void mp_ps_print_scaled (MP mp,scaled s) {
251 scaled delta; /* amount of allowable inaccuracy */
253 mp_ps_print_char(mp, '-');
254 negate(s); /* print the sign, if negative */
256 mp_ps_print_int(mp, s / unity); /* print the integer part */
260 mp_ps_print_char(mp, '.');
263 s=s+0100000-(delta / 2); /* round the final digit */
264 mp_ps_print_char(mp, '0'+(s / unity));
272 @* \[44a] Dealing with font encodings.
274 First, here are a few helpers for parsing files
276 @d check_buf(size, buf_size)
277 if ((unsigned)(size) > (unsigned)(buf_size)) {
279 mp_snprintf(S,128,"buffer overflow: (%d,%d) at file %s, line %d",
280 size,buf_size, __FILE__, __LINE__ );
281 mp_fatal_error(mp,S);
284 @d append_char_to_buf(c, p, buf, buf_size) do {
287 if (c == 13 || c == EOF)
289 if (c != ' ' || (p > buf && p[-1] != 32)) {
290 check_buf(p - buf + 1, (buf_size));
295 @d append_eol(p, buf, buf_size) do {
296 check_buf(p - buf + 2, (buf_size));
297 if (p - buf > 1 && p[-1] != 10)
299 if (p - buf > 2 && p[-2] == 32) {
306 @d remove_eol(p, buf) do {
312 @d skip(p, c) if (*p == c) p++
313 @d strend(s) strchr(s, 0)
314 @d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
319 boolean loaded; /* the encoding has been loaded? */
320 char *file_name; /* encoding file name */
321 char *enc_name; /* encoding true name */
322 integer objnum; /* object number */
324 integer tounicode; /* object number of associated ToUnicode entry */
334 #define ENC_BUF_SIZE 0x1000
335 char enc_line[ENC_BUF_SIZE];
339 @d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file)
340 @d enc_close() (mp->close_file)(mp,mp->ps->enc_file)
343 int enc_getchar(MP mp) {
345 unsigned char abyte=0;
346 void *byte_ptr = &abyte;
347 (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len);
352 static boolean mp_enc_open (MP mp, char *n) {
353 mp->ps->enc_file=(mp->open_file)(mp,n, "r", mp_filetype_encoding);
354 if (mp->ps->enc_file!=NULL)
359 static void mp_enc_getline (MP mp) {
364 print_err("unexpected end of file");
367 p = mp->ps->enc_line;
369 c = enc_getchar (mp);
370 append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
372 append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
373 if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
376 static void mp_load_enc (MP mp, char *enc_name,
377 char **enc_encname, char **glyph_names){
378 char buf[ENC_BUF_SIZE], *p, *r;
381 int save_selector = mp->selector;
382 if (!mp_enc_open (mp,enc_name)) {
383 mp_print (mp,"cannot open encoding file for reading");
386 mp_normalize_selector(mp);
388 mp_print (mp, enc_name);
390 if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
391 remove_eol (r, mp->ps->enc_line);
392 print_err ("invalid encoding vector (a name or `[' missing): `");
393 mp_print(mp,mp->ps->enc_line);
397 while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
398 myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
399 memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
400 *(myname+(r-mp->ps->enc_line-1))=0;
401 *enc_encname = myname;
409 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
412 if (names_count > 256) {
413 print_err ("encoding vector contains more than 256 names");
416 if (mp_xstrcmp (buf, notdef) != 0)
417 glyph_names[names_count] = mp_xstrdup (mp,buf);
420 if (*r != 10 && *r != '%') {
421 if (str_prefix (r, "] def"))
424 remove_eol (r, mp->ps->enc_line);
426 ("invalid encoding vector: a name or `] def' expected: `");
427 mp_print(mp,mp->ps->enc_line);
433 r = mp->ps->enc_line;
438 mp->selector = save_selector;
440 static void mp_read_enc (MP mp, enc_entry * e) {
444 mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
448 @ |write_enc| is used to write either external encoding (given in map file) or
449 internal encoding (read from the font file);
450 the 2nd argument is a pointer to the encoding entry;
453 static void mp_write_enc (MP mp, enc_entry * e) {
458 if (e->objnum != 0) /* the encoding has been written already */
463 mp_ps_print(mp,"\n%%%%BeginResource: encoding ");
464 mp_ps_print(mp, e->enc_name);
465 mp_ps_print_nl(mp, "/");
466 mp_ps_print(mp, e->enc_name);
467 mp_ps_print(mp, " [ ");
468 foffset = strlen(e->file_name)+3;
469 for (i = 0; i < 256; i++) {
471 if (s+1+foffset>=80) {
476 mp_ps_print_char(mp,'/');
477 mp_ps_print(mp, g[i]);
478 mp_ps_print_char(mp,' ');
482 mp_ps_print_nl (mp,"] def\n");
483 mp_ps_print(mp,"%%%%EndResource");
487 @ All encoding entries go into AVL tree for fast search by name.
490 struct avl_table *enc_tree;
492 @ Memory management functions for avl
494 @<Static variables in the outer block@>=
495 static const char notdef[] = ".notdef";
498 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
499 static void avl_xfree (struct libavl_allocator *allocator, void *block);
502 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
504 return malloc (size);
506 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
512 struct libavl_allocator avl_xallocator;
514 @ @<Set initial...@>=
515 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
516 mp->ps->avl_xallocator.libavl_free= avl_xfree;
517 mp->ps->enc_tree = NULL;
520 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
522 return strcmp (((const enc_entry *) pa)->file_name,
523 ((const enc_entry *) pb)->file_name);
525 static enc_entry * mp_add_enc (MP mp, char *s) {
529 if (mp->ps->enc_tree == NULL) {
530 mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
533 p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
534 if (p != NULL) /* encoding already registered */
536 p = mp_xmalloc (mp,1,sizeof (enc_entry));
538 p->file_name = mp_xstrdup (mp,s);
541 p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
542 for (i = 0; i < 256; i++)
543 p->glyph_names[i] = (char *) notdef;
544 aa = avl_probe (mp->ps->enc_tree, p);
551 static void mp_destroy_enc_entry (void *pa, void *pb) {
554 p = (enc_entry *) pa;
556 mp_xfree (p->file_name);
557 if (p->glyph_names != NULL)
558 for (i = 0; i < 256; i++)
559 if (p->glyph_names[i] != notdef)
560 mp_xfree (p->glyph_names[i]);
561 mp_xfree (p->glyph_names);
566 static void enc_free (MP mp);
568 @ @c static void enc_free (MP mp) {
569 if (mp->ps->enc_tree != NULL)
570 avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
573 @ @<Exported function headers@>=
574 void mp_reload_encodings (MP mp) ;
577 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) ;
579 @ @c void mp_reload_encodings (MP mp) {
583 int lastfnum = mp->last_fnum;
584 for (f=null_font+1;f<=lastfnum;f++) {
585 if (mp->font_enc_name[f]!=NULL ) {
586 mp_xfree(mp->font_enc_name[f]);
587 mp->font_enc_name[f]=NULL;
589 if (mp_has_fm_entry (mp,f,&fm_cur)) {
590 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
591 e = fm_cur->encoding;
597 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) {
601 for (f=null_font+1;f<=lastfnum;f++) {
602 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
603 if (fm != NULL && (fm->ps_name != NULL)) {
604 if (is_reencoded (fm)) {
605 if (encodings_only || (!is_subsetted (fm))) {
607 mp_write_enc (mp, e);
608 /* clear for next run */
617 @* \[44b] Parsing font map files.
625 @d fm_close() (mp->close_file)(mp,mp->ps->fm_file)
626 @d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file)
629 int fm_getchar(MP mp) {
631 unsigned char abyte=0;
632 void *byte_ptr = &abyte;
633 (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
641 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
642 enum _ltype { MAPFILE, MAPLINE };
643 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
644 typedef struct mitem {
645 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
646 int type; /* map file or map line */
647 char *map_line; /* pointer to map file name or map line */
648 int lineno; /* line number in map file */
654 fm_entry *loaded_tfm_found;
655 fm_entry *avail_tfm_found;
656 fm_entry *non_tfm_found;
657 fm_entry *not_avail_tfm_found;
659 @ @<Set initial...@>=
660 mp->ps->mitem = NULL;
663 static const char nontfm[] = "<nontfm>";
666 @d read_field(r, q, buf) do {
668 while (*r != ' ' && *r != '\0')
676 fm->F = mp_xstrdup(mp,buf);
688 static fm_entry *new_fm_entry (MP mp) {
690 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
695 fm->subset_tag = NULL;
697 fm->tfm_num = null_font;
698 fm->tfm_avail = TFM_UNCHECKED;
706 fm->all_glyphs = false;
713 static void delete_fm_entry (fm_entry * fm) {
714 mp_xfree (fm->tfm_name);
715 mp_xfree (fm->ps_name);
716 mp_xfree (fm->ff_name);
717 mp_xfree (fm->subset_tag);
718 mp_xfree (fm->charset);
722 static ff_entry *new_ff_entry (MP mp) {
724 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
730 static void delete_ff_entry (ff_entry * ff) {
731 mp_xfree (ff->ff_name);
732 mp_xfree (ff->ff_path);
736 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
737 static char buf[SMALL_BUF_SIZE];
738 char *p = tfmname, *r = strend (p) - 1, *q = r;
739 while (q > p && isdigit (*q))
741 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
743 check_buf (q - p + 1, SMALL_BUF_SIZE);
744 strncpy (buf, p, (size_t) (q - p));
750 @ @<Exported function headers@>=
751 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
754 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
755 fm_entry *res = NULL;
756 res = mp_fm_lookup (mp, f);
760 return (res != NULL);
764 struct avl_table *tfm_tree;
765 struct avl_table *ps_tree;
766 struct avl_table *ff_tree;
768 @ @<Set initial...@>=
769 mp->ps->tfm_tree = NULL;
770 mp->ps->ps_tree = NULL;
771 mp->ps->ff_tree = NULL;
773 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
776 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
778 return strcmp (((const fm_entry *) pa)->tfm_name,
779 ((const fm_entry *) pb)->tfm_name);
782 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
784 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
786 const fm_entry *p1 = (const fm_entry *) pa;
787 const fm_entry *p2 = (const fm_entry *) pb;
789 assert (p1->ps_name != NULL && p2->ps_name != NULL);
790 if ((i = strcmp (p1->ps_name, p2->ps_name)))
792 cmp_return (p1->slant, p2->slant);
793 cmp_return (p1->extend, p2->extend);
794 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
795 (i = strcmp (p1->tfm_name, p2->tfm_name)))
800 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
802 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
804 return strcmp (((const ff_entry *) pa)->ff_name,
805 ((const ff_entry *) pb)->ff_name);
808 @ @c static void create_avl_trees (MP mp) {
809 if (mp->ps->tfm_tree == NULL) {
810 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
811 assert (mp->ps->tfm_tree != NULL);
813 if (mp->ps->ps_tree == NULL) {
814 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
815 assert (mp->ps->ps_tree != NULL);
817 if (mp->ps->ff_tree == NULL) {
818 mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
819 assert (mp->ps->ff_tree != NULL);
823 @ The function |avl_do_entry| is not completely symmetrical with regards
824 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
825 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
826 with the original version.
830 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
831 @d set_pslink(fm) ((fm)->links |= LINK_PS)
832 @d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
833 @d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
834 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
835 @d has_pslink(fm) ((fm)->links & LINK_PS)
838 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
844 /* handle |tfm_name| link */
846 if (strcmp (fp->tfm_name, nontfm)) {
847 p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
849 if (mode == FM_DUPIGNORE) {
850 mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
854 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
855 if (mp_has_font_size(mp,p->tfm_num)) {
857 "fontmap entry for `%s' has been used, replace/delete not allowed",
862 a = avl_delete (mp->ps->tfm_tree, p);
869 if (mode != FM_DELETE) {
870 aa = avl_probe (mp->ps->tfm_tree, fp);
876 /* handle |ps_name| link */
878 if (fp->ps_name != NULL) {
879 assert (fp->tfm_name != NULL);
880 p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
882 if (mode == FM_DUPIGNORE) {
884 "ps_name entry for `%s' already exists, duplicates ignored",
888 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
889 if (mp_has_font_size(mp,p->tfm_num)) {
890 /* REPLACE/DELETE not allowed */
892 "fontmap entry for `%s' has been used, replace/delete not allowed",
897 a = avl_delete (mp->ps->ps_tree, p);
900 if (!has_tfmlink (p))
904 if (mode != FM_DELETE) {
905 aa = avl_probe (mp->ps->ps_tree, fp);
911 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
912 return 1; /* deallocation of |fm_entry| structure required */
917 @ consistency check for map entry, with warn flag
920 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
924 if (fm->ps_name != NULL) {
925 if (is_basefont (fm)) {
926 if (is_fontfile (fm) && !is_included (fm)) {
928 mp_snprintf(s,128, "invalid entry for `%s': "
929 "font file must be included or omitted for base fonts",
935 } else { /* not a base font */
936 /* if no font file given, drop this entry */
937 /* |if (!is_fontfile (fm)) {
940 "invalid entry for `%s': font file missing",
949 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
952 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
958 if ((fm->slant != 0 || fm->extend != 0) &&
959 (is_truetype (fm))) {
962 "invalid entry for `%s': "
963 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
969 if (abs (fm->slant) > 1000) {
972 "invalid entry for `%s': too big value of SlantFont (%g)",
973 fm->tfm_name, fm->slant / 1000.0);
978 if (abs (fm->extend) > 2000) {
981 "invalid entry for `%s': too big value of ExtendFont (%g)",
982 fm->tfm_name, fm->extend / 1000.0);
988 !(is_truetype (fm) && is_included (fm) &&
989 is_subsetted (fm) && !is_reencoded (fm))) {
992 "invalid entry for `%s': "
993 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
1002 @ returns true if s is one of the 14 std. font names; speed-trimmed.
1004 @c static boolean check_basefont (char *s) {
1005 static const char *basefont_names[] = {
1006 "Courier", /* 0:7 */
1007 "Courier-Bold", /* 1:12 */
1008 "Courier-Oblique", /* 2:15 */
1009 "Courier-BoldOblique", /* 3:19 */
1010 "Helvetica", /* 4:9 */
1011 "Helvetica-Bold", /* 5:14 */
1012 "Helvetica-Oblique", /* 6:17 */
1013 "Helvetica-BoldOblique", /* 7:21 */
1015 "Times-Roman", /* 9:11 */
1016 "Times-Bold", /* 10:10 */
1017 "Times-Italic", /* 11:12 */
1018 "Times-BoldItalic", /* 12:16 */
1019 "ZapfDingbats" /* 13:12 */
1021 static const int Index[] =
1022 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1025 const size_t n = strlen (s);
1029 if (n == 12) { /* three names have length 12 */
1032 k = 1; /* Courier-Bold */
1035 k = 11; /* Times-Italic */
1038 k = 13; /* ZapfDingbats */
1045 if (k > -1 && !strcmp (basefont_names[k], s))
1051 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1053 @c static void fm_scan_line (MP mp) {
1054 int a, b, c, j, u = 0, v = 0;
1057 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1058 char *p, *q, *r, *s;
1060 switch (mp->ps->mitem->type) {
1064 c = fm_getchar (mp);
1065 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1071 r = mp->ps->mitem->map_line;
1076 if (*r == '\0' || is_cfg_comment (*r))
1078 fm = new_fm_entry (mp);
1079 read_field (r, q, buf);
1080 set_field (tfm_name);
1082 read_field (r, q, buf);
1083 if (*buf != '<' && *buf != '"')
1084 set_field (ps_name);
1086 r = p; /* unget the field */
1087 if (isdigit (*r)) { /* font flags given */
1088 fm->flags = atoi (r);
1089 while (isdigit (*r))
1092 while (1) { /* loop through "specials", encoding, font file */
1097 case '"': /* opening quote */
1102 if (sscanf (r, "%f %n", &d, &j) > 0) {
1103 s = r + j; /* jump behind number, eat also blanks, if any */
1104 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1105 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1106 if (str_prefix (s, "SlantFont")) {
1107 d *= 1000.0; /* correct rounding also for neg. numbers */
1108 fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1109 r = s + strlen ("SlantFont");
1110 } else if (str_prefix (s, "ExtendFont")) {
1112 fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1113 if (fm->extend == 1000)
1115 r = s + strlen ("ExtendFont");
1116 } else { /* unknown name */
1118 *r != ' ' && *r != '"' && *r != '\0';
1119 r++); /* jump over name */
1120 c = *r; /* remember char for temporary end of string */
1122 mp_snprintf(warn_s,128,
1123 "invalid entry for `%s': unknown name `%s' ignored",
1129 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1132 if (*r == '"') /* closing quote */
1135 mp_snprintf(warn_s,128,
1136 "invalid entry for `%s': closing quote missing",
1142 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1143 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1149 default: /* encoding or font file specification */
1153 if (*r == '<' || *r == '[')
1156 read_field (r, q, buf);
1157 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1158 if (strlen (buf) > 4 && strcasecmp (strend (buf) - 4, ".enc") == 0) {
1159 fm->encoding = mp_add_enc (mp, buf);
1160 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1161 } else if (strlen (buf) > 0) { /* file name given */
1162 /* font file, formats:
1163 * subsetting: '<cmr10.pfa'
1164 * no subsetting: '<<cmr10.pfa'
1165 * no embedding: 'cmr10.pfa'
1167 if (a == '<' || u == '<') {
1169 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1171 /* otherwise b == '<' (or '[') => no subsetting */
1173 set_field (ff_name);
1182 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1184 if (is_fontfile (fm)
1185 && strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1187 if (check_fm_entry (mp,fm, true) != 0)
1190 Until here the map line has been completely scanned without errors;
1191 fm points to a valid, freshly filled-out |fm_entry| structure.
1192 Now follows the actual work of registering/deleting.
1194 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
1197 delete_fm_entry (fm);
1201 @c static void fm_read_info (MP mp) {
1204 if (mp->ps->tfm_tree == NULL)
1205 create_avl_trees (mp);
1206 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1208 mp->ps->mitem->lineno = 1;
1209 switch (mp->ps->mitem->type) {
1211 n = mp->ps->mitem->map_line;
1212 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1213 if (!mp->ps->fm_file) {
1214 mp_snprintf(s,256,"cannot open font map file %s",n);
1217 int save_selector = mp->selector;
1218 mp_normalize_selector(mp);
1221 while (!fm_eof ()) {
1223 mp->ps->mitem->lineno++;
1227 mp->selector = save_selector;
1228 mp->ps->fm_file = NULL;
1238 mp->ps->mitem->map_line = NULL; /* done with this line */
1242 @ @c static void init_fm (fm_entry * fm, font_number f) {
1243 if (fm->tfm_num == null_font ) {
1245 fm->tfm_avail = TFM_FOUND;
1250 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1253 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1258 if (mp->ps->tfm_tree == NULL)
1259 fm_read_info (mp); /* only to read default map file */
1260 tfm = mp->font_name[f];
1261 assert (strcmp (tfm, nontfm));
1262 /* Look up for full <tfmname>[+-]<expand> */
1264 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1267 return (fm_entry *) fm;
1269 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1270 if (tfm == NULL) /* not an expanded font, nothing to do */
1274 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1275 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1276 return (fm_entry *) fm; /* font expansion uses the base font */
1281 @ Early check whether a font file exists. Used e. g. for replacing fonts
1282 of embedded PDF files: Without font file, the font within the embedded
1283 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1284 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1285 only once per font file name + expansion parameter. This might help
1286 keeping speed, if many PDF pages with same fonts are to be embedded.
1288 The |ff_tree| contains only font files, which are actually needed,
1289 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1292 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1297 assert (fm->ff_name != NULL);
1298 tmp.ff_name = fm->ff_name;
1299 ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1300 if (ff == NULL) { /* not yet in database */
1301 ff = new_ff_entry (mp);
1302 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1303 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1304 aa = avl_probe (mp->ps->ff_tree, ff);
1305 assert (aa != NULL);
1310 @ Process map file given by its name or map line contents. Items not
1311 beginning with [+-=] flush default map file, if it has not yet been
1312 read. Leading blanks and blanks immediately following [+-=] are ignored.
1315 @c static void mp_process_map_item (MP mp, char *s, int type) {
1319 s++; /* ignore leading blank */
1321 case '+': /* +mapfile.map, +mapline */
1322 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1325 case '=': /* =mapfile.map, =mapline */
1326 mode = FM_REPLACE; /* try to replace earlier entry */
1329 case '-': /* -mapfile.map, -mapline */
1330 mode = FM_DELETE; /* try to delete entry */
1334 mode = FM_DUPIGNORE; /* like +, but also: */
1335 mp_xfree(mp->ps->mitem->map_line);
1336 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1339 s++; /* ignore blank after [+-=] */
1340 p = s; /* map item starts here */
1342 case MAPFILE: /* remove blank at end */
1343 while (*p != '\0' && *p != ' ')
1347 case MAPLINE: /* blank at end allowed */
1352 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1354 if (*s != '\0') { /* only if real item to process */
1355 mp->ps->mitem->mode = mode;
1356 mp->ps->mitem->type = type;
1357 mp->ps->mitem->map_line = s;
1362 @ @<Exported function headers@>=
1363 void mp_map_file (MP mp, str_number t);
1364 void mp_map_line (MP mp, str_number t);
1365 void mp_init_map_file (MP mp, int is_troff);
1368 void mp_map_file (MP mp, str_number t) {
1369 char *s = mp_xstrdup(mp,mp_str (mp,t));
1370 mp_process_map_item (mp, s, MAPFILE);
1373 void mp_map_line (MP mp, str_number t) {
1374 char *s = mp_xstrdup(mp,mp_str (mp,t));
1375 mp_process_map_item (mp, s, MAPLINE);
1380 @c void mp_init_map_file (MP mp, int is_troff) {
1382 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1383 mp->ps->mitem->mode = FM_DUPIGNORE;
1384 mp->ps->mitem->type = MAPFILE;
1385 mp->ps->mitem->map_line = NULL;
1386 r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap);
1389 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1392 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1394 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1399 @ @<Dealloc variables@>=
1400 if (mp->ps->mitem!=NULL) {
1401 mp_xfree(mp->ps->mitem->map_line);
1402 mp_xfree(mp->ps->mitem);
1408 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1411 fm = (fm_entry *) pa;
1412 if (!has_pslink (fm))
1413 delete_fm_entry (fm);
1417 static void destroy_fm_entry_ps (void *pa, void *pb) {
1420 fm = (fm_entry *) pa;
1421 if (!has_tfmlink (fm))
1422 delete_fm_entry (fm);
1426 static void destroy_ff_entry (void *pa, void *pb) {
1429 ff = (ff_entry *) pa;
1430 delete_ff_entry (ff);
1434 static void fm_free (MP mp);
1437 static void fm_free (MP mp) {
1438 if (mp->ps->tfm_tree != NULL)
1439 avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1440 if (mp->ps->ps_tree != NULL)
1441 avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1442 if (mp->ps->ff_tree != NULL)
1443 avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1446 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1447 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1448 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
1449 one or more spaces, a PostScript name, and possibly a space and some other junk.
1450 This routine reads the table, updates |font_ps_name| entries starting after
1451 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
1453 @d ps_tab_name "psfonts.map" /* locates font name translation table */
1456 static void mp_read_psname_table (MP mp) ;
1459 static void mp_read_psname_table (MP mp) {
1461 if (mp->ps->mitem == NULL) {
1462 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1463 mp->ps->mitem->mode = FM_DUPIGNORE;
1464 mp->ps->mitem->type = MAPFILE;
1465 mp->ps->mitem->map_line = NULL;
1467 mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name);
1469 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1470 if (mp_has_fm_entry(mp, k, NULL)) {
1471 mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1474 mp->last_ps_fnum=mp->last_fnum;
1478 @ The traditional function is a lot shorter now.
1482 @* \[44c] Helper functions for Type1 fonts.
1485 typedef char char_entry;
1486 typedef unsigned char Byte;
1490 char_entry *char_ptr, *char_array;
1492 char *job_id_string;
1494 @ @<Set initial...@>=
1495 mp->ps->char_array = NULL;
1496 mp->ps->job_id_string = NULL;
1499 @d SMALL_ARRAY_SIZE 256
1503 void mp_set_job_id (MP mp) {
1504 char *name_string, *format_string, *s;
1506 if (mp->ps->job_id_string != NULL)
1508 if ( mp->job_name==NULL )
1509 mp->job_name = mp_xstrdup(mp,"mpout");
1510 name_string = mp_xstrdup (mp,mp->job_name);
1511 format_string = mp_xstrdup (mp,mp->mem_ident);
1512 slen = SMALL_BUF_SIZE +
1513 strlen (name_string) +
1514 strlen (format_string);
1515 s = mp_xmalloc (mp,slen, sizeof (char));
1516 sprintf (s,"%.4d/%.2d/%.2d %.2d:%.2d %s %s",
1517 (mp->internal[mp_year]>>16),
1518 (mp->internal[mp_month]>>16),
1519 (mp->internal[mp_day]>>16),
1520 (mp->internal[mp_time]>>16) / 60,
1521 (mp->internal[mp_time]>>16) % 60,
1522 name_string, format_string);
1523 mp->ps->job_id_string = mp_xstrdup (mp,s);
1525 mp_xfree (name_string);
1526 mp_xfree (format_string);
1528 static void fnstr_append (MP mp, const char *s) {
1529 size_t l = strlen (s) + 1;
1530 alloc_array (char, l, SMALL_ARRAY_SIZE);
1531 strcat (mp->ps->char_ptr, s);
1532 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1535 @ @<Exported function headers@>=
1536 void mp_set_job_id (MP mp) ;
1538 @ @<Dealloc variables@>=
1539 mp_xfree(mp->ps->job_id_string);
1541 @ this is not really a true crc32, but it should be just enough to keep
1542 subsets prefixes somewhat disjunct
1545 static unsigned long crc32 (int oldcrc, const Byte *buf, int len) {
1546 unsigned long ret = 0;
1549 ret = (23<<24)+(45<<16)+(67<<8)+89;
1552 ret = (ret<<2)+buf[i];
1555 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1556 integer b; /* |char_base[f]| */
1558 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1564 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, int tex_font)
1570 if (mp->ps->job_id_string ==NULL)
1571 mp_fatal_error(mp, "no job id!");
1572 l = strlen (mp->ps->job_id_string) + 1;
1574 alloc_array (char, l, SMALL_ARRAY_SIZE);
1575 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1576 mp->ps->char_ptr = strend (mp->ps->char_array);
1577 if (fm_cur->tfm_name != NULL) {
1578 fnstr_append (mp," TFM name: ");
1579 fnstr_append (mp,fm_cur->tfm_name);
1581 fnstr_append (mp," PS name: ");
1582 if (fm_cur->ps_name != NULL)
1583 fnstr_append (mp,fm_cur->ps_name);
1584 fnstr_append (mp," Encoding: ");
1585 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1586 fnstr_append (mp,(fm_cur->encoding)->file_name);
1588 fnstr_append (mp,"built-in");
1589 fnstr_append (mp," CharSet: ");
1590 for (i = 0; i < 256; i++)
1591 if (mp_char_marked (mp,tex_font, i) && glyph_names[i] != notdef) {
1592 if (glyph_names[i]!=NULL) {
1593 fnstr_append (mp,"/");
1594 fnstr_append (mp,glyph_names[i]);
1597 if (fm_cur->charset != NULL) {
1598 fnstr_append (mp," Extra CharSet: ");
1599 fnstr_append (mp, fm_cur->charset);
1601 crc = crc32 (0L, Z_NULL, 0);
1602 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1603 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1604 * there are 26 uppercase chars ==> each char represents a number in range
1605 * |0..25|. The maximal number that can be represented by the tag is
1606 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1607 * of the CRC must be dropped out.
1609 for (i = 0; i < 6; i++) {
1610 tag[i] = 'A' + crc % 26;
1614 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1620 @d external_enc() (fm_cur->encoding)->glyph_names
1621 @d is_used_char(c) mp_char_marked (mp, tex_font, c)
1622 @d end_last_eexec_line()
1623 mp->ps->hexline_length = HEXLINE_WIDTH;
1625 mp->ps->t1_eexec_encrypt = false
1626 @d t1_log(s) mp_print(mp,(char *)s)
1627 @d t1_putchar(c) wps_chr(c)
1628 @d embed_all_glyphs(tex_font) false
1630 @d extra_charset() mp->ps->dvips_extra_charset
1631 @d update_subset_tag()
1632 @d fixedcontent true
1635 #define PRINTF_BUF_SIZE 1024
1636 char *dvips_extra_charset;
1638 unsigned char *grid;
1639 char *ext_glyph_names[256];
1640 char print_buf[PRINTF_BUF_SIZE];
1641 int t1_byte_waiting;
1643 @ @<Set initial ...@>=
1644 mp->ps->dvips_extra_charset=NULL;
1645 mp->ps->t1_byte_waiting=0;
1648 @d t1_ungetchar(A) mp->ps->t1_byte_waiting=A
1649 @d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file)
1650 @d t1_close() (mp->close_file)(mp,mp->ps->t1_file)
1651 @d valid_code(c) (c >= 0 && c < 256)
1654 int t1_getchar (MP mp) {
1656 unsigned char abyte=0;
1657 void *byte_ptr = &abyte;
1658 if (mp->ps->t1_byte_waiting) {
1659 abyte = mp->ps->t1_byte_waiting;
1660 mp->ps->t1_byte_waiting = 0;
1662 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
1667 @ @<Static variables in the outer block@>=
1668 static const char *standard_glyph_names[256] =
1669 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1670 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1671 notdef, notdef, notdef, notdef, notdef, notdef,
1672 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1673 "space", "exclam", "quotedbl", "numbersign",
1674 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1675 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1676 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1677 "eight", "nine", "colon", "semicolon", "less",
1678 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1679 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1680 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1681 "backslash", "bracketright", "asciicircum", "underscore",
1682 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1683 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1684 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1685 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1686 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1687 notdef, notdef, notdef, notdef, notdef, notdef,
1688 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1689 notdef, notdef, notdef, "exclamdown", "cent",
1690 "sterling", "fraction", "yen", "florin", "section", "currency",
1691 "quotesingle", "quotedblleft", "guillemotleft",
1692 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1693 "dagger", "daggerdbl", "periodcentered", notdef,
1694 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1695 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1696 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1697 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1698 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1699 notdef, notdef, notdef, notdef, notdef, notdef,
1700 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1701 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1702 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1703 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1704 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1705 "germandbls", notdef, notdef, notdef, notdef };
1706 static const char charstringname[] = "/CharStrings";
1709 char **t1_glyph_names;
1710 char *t1_builtin_glyph_names[256];
1711 char charsetstr[0x4000];
1712 boolean read_encoding_only;
1716 #define T1_BUF_SIZE 0x10
1720 #define CS_VMOVETO 4
1721 #define CS_RLINETO 5
1722 #define CS_HLINETO 6
1723 #define CS_VLINETO 7
1724 #define CS_RRCURVETO 8
1725 #define CS_CLOSEPATH 9
1726 #define CS_CALLSUBR 10
1727 #define CS_RETURN 11
1728 #define CS_ESCAPE 12
1730 #define CS_ENDCHAR 14
1731 #define CS_RMOVETO 21
1732 #define CS_HMOVETO 22
1733 #define CS_VHCURVETO 30
1734 #define CS_HVCURVETO 31
1735 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1737 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1738 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1739 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1740 #define CS_SEAC CS_1BYTE_MAX + 6
1741 #define CS_SBW CS_1BYTE_MAX + 7
1742 #define CS_DIV CS_1BYTE_MAX + 12
1743 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1744 #define CS_POP CS_1BYTE_MAX + 17
1745 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1746 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1747 #define CS_MAX CS_2BYTE_MAX
1750 typedef unsigned char byte;
1752 byte nargs; /* number of arguments */
1753 boolean bottom; /* take arguments from bottom of stack? */
1754 boolean clear; /* clear stack? */
1756 } cc_entry; /* CharString Command */
1758 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1760 unsigned short len; /* length of the whole string */
1761 unsigned short cslen; /* length of the encoded part of the string */
1771 unsigned short t1_dr, t1_er;
1772 unsigned short t1_cslen;
1776 typedef char t1_line_entry;
1777 typedef char t1_buf_entry;
1780 t1_line_entry *t1_line_ptr, *t1_line_array;
1781 size_t t1_line_limit;
1782 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1783 size_t t1_buf_limit;
1785 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1786 char *cs_dict_start, *cs_dict_end;
1787 int cs_count, cs_size, cs_size_pos;
1789 char *subr_array_start, *subr_array_end;
1790 int subr_max, subr_size, subr_size_pos;
1792 @ @<Set initial...@>=
1793 mp->ps->t1_line_array = NULL;
1794 mp->ps->t1_buf_array = NULL;
1797 This list contains the begin/end tokens commonly used in the
1798 /Subrs array of a Type 1 font.
1800 @<Static variables in the outer block@>=
1801 static const char *cs_token_pairs_list[][2] = {
1804 {" RD", "noaccess put"},
1805 {" -|", "noaccess put"},
1810 const char **cs_token_pair;
1811 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1812 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1813 long t1_block_length;
1821 @<Set initial ...@>=
1822 mp->ps->hexline_length = 0;
1825 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1826 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1827 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1828 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1829 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1830 @d t1_subrs() t1_prefix("/Subrs")
1831 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1832 @d t1_cleartomark() t1_prefix("cleartomark")
1834 @d isdigit(A) ((A)>='0'&&(A)<='9')
1837 static void end_hexline (MP mp) {
1838 if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
1840 mp->ps->hexline_length = 0;
1843 static void t1_check_pfa (MP mp) {
1844 const int c = t1_getchar (mp);
1845 mp->ps->t1_pfa = (c != 128) ? true : false;
1848 static int t1_getbyte (MP mp)
1850 int c = t1_getchar (mp);
1853 if (mp->ps->t1_block_length == 0) {
1855 mp_fatal_error (mp, "invalid marker");
1856 c = t1_getchar (mp);
1862 mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
1863 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 8;
1864 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 16;
1865 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 24;
1866 c = t1_getchar (mp);
1868 mp->ps->t1_block_length--;
1871 static int hexval (int c) {
1872 if (c >= 'A' && c <= 'F')
1873 return c - 'A' + 10;
1874 else if (c >= 'a' && c <= 'f')
1875 return c - 'a' + 10;
1876 else if (c >= '0' && c <= '9')
1881 static byte edecrypt (MP mp, byte cipher) {
1883 if (mp->ps->t1_pfa) {
1884 while (cipher == 10 || cipher == 13)
1885 cipher = t1_getbyte (mp);
1886 mp->ps->last_hexbyte = cipher = (hexval (cipher) << 4) + hexval (t1_getbyte (mp));
1888 plain = (cipher ^ (mp->ps->t1_dr >> 8));
1889 mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * t1_c1 + t1_c2;
1892 static byte cdecrypt (byte cipher, unsigned short *cr)
1894 const byte plain = (cipher ^ (*cr >> 8));
1895 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1898 static byte eencrypt (MP mp, byte plain)
1900 const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1901 mp->ps->t1_er = (cipher + mp->ps->t1_er) * t1_c1 + t1_c2;
1905 static byte cencrypt (byte plain, unsigned short *cr)
1907 const byte cipher = (plain ^ (*cr >> 8));
1908 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1912 static char *eol (char *s) {
1913 char *p = strend (s);
1914 if (p - s > 1 && p[-1] != 10) {
1920 static float t1_scan_num (MP mp, char *p, char **r)
1925 if (sscanf (p, "%g", &f) != 1) {
1926 remove_eol (p, mp->ps->t1_line_array);
1927 mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1928 mp_fatal_error(mp,s);
1931 for (; isdigit (*p) || *p == '.' ||
1932 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1938 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1941 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1944 while (s1 >= begin_buf && s2 >= s) {
1953 @d alloc_array(T, n, s) do {
1954 if (mp->ps->T##_array == NULL) {
1955 mp->ps->T##_limit = (s);
1956 if ((unsigned)(n) > mp->ps->T##_limit)
1957 mp->ps->T##_limit = (n);
1958 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
1959 mp->ps->T##_ptr = mp->ps->T##_array;
1961 else if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
1962 size_t last_ptr_index;
1963 last_ptr_index = mp->ps->T##_ptr - mp->ps->T##_array;
1964 mp->ps->T##_limit *= 2;
1965 if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
1966 mp->ps->T##_limit = mp->ps->T##_ptr - mp->ps->T##_array + (n);
1967 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
1968 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
1972 @d out_eexec_char(A) t1_outhex(mp,(A))
1975 static void t1_outhex (MP mp, byte b)
1977 static const char *hexdigits = "0123456789ABCDEF";
1978 t1_putchar (hexdigits[b / 16]);
1979 t1_putchar (hexdigits[b % 16]);
1980 mp->ps->hexline_length += 2;
1983 static void t1_getline (MP mp) {
1984 int c, l, eexec_scan;
1986 static const char eexec_str[] = "currentfile eexec";
1987 static int eexec_len = 17; /* |strlen(eexec_str)| */
1990 mp_fatal_error (mp,"unexpected end of file");
1991 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
1992 alloc_array (t1_line, 1, T1_BUF_SIZE);
1993 mp->ps->t1_cslen = 0;
1995 c = t1_getbyte (mp);
1998 while (!t1_eof ()) {
1999 if (mp->ps->t1_in_eexec == 1)
2000 c = edecrypt (mp,c);
2001 alloc_array (t1_line, 1, T1_BUF_SIZE);
2002 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2003 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
2004 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
2009 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2011 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
2012 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2013 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2014 p = mp->ps->t1_line_ptr - 5;
2017 mp->ps->t1_cslen = l = t1_scan_num (mp, p + 1, 0);
2018 mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2019 /* |mp->ps->cs_start| is an index now */
2020 alloc_array (t1_line, l, T1_BUF_SIZE);
2022 *mp->ps->t1_line_ptr++ = edecrypt (mp,t1_getbyte (mp));
2024 c = t1_getbyte (mp);
2026 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
2027 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2028 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2030 if (eexec_scan == eexec_len)
2031 mp->ps->t1_in_eexec = 1;
2033 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2034 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2035 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2038 static void t1_putline (MP mp)
2040 char *p = mp->ps->t1_line_array;
2041 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2043 if (mp->ps->t1_eexec_encrypt) {
2044 while (p < mp->ps->t1_line_ptr)
2045 out_eexec_char (eencrypt (mp,*p++));
2047 while (p < mp->ps->t1_line_ptr)
2052 static void t1_puts (MP mp, const char *s)
2054 if (s != mp->ps->t1_line_array)
2055 strcpy (mp->ps->t1_line_array, s);
2056 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2060 static void t1_printf (MP mp, const char *fmt, ...)
2063 va_start (args, fmt);
2064 vsprintf (mp->ps->t1_line_array, fmt, args);
2065 t1_puts (mp,mp->ps->t1_line_array);
2069 static void t1_init_params (MP mp, const char *open_name_prefix,
2070 char *cur_file_name) {
2071 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2072 t1_log (open_name_prefix);
2073 t1_log (cur_file_name);
2075 mp->ps->t1_lenIV = 4;
2076 mp->ps->t1_dr = 55665;
2077 mp->ps->t1_er = 55665;
2078 mp->ps->t1_in_eexec = 0;
2079 mp->ps->t1_cs = false;
2080 mp->ps->t1_scan = true;
2081 mp->ps->t1_synthetic = false;
2082 mp->ps->t1_eexec_encrypt = false;
2083 mp->ps->t1_block_length = 0;
2086 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2087 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2088 t1_log (close_name_suffix);
2093 static void t1_check_block_len (MP mp, boolean decrypt) {
2096 if (mp->ps->t1_block_length == 0)
2098 c = t1_getbyte (mp);
2100 c = edecrypt (mp,c);
2101 l = mp->ps->t1_block_length;
2102 if (!(l == 0 && (c == 10 || c == 13))) {
2103 mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2109 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2111 if (!mp->ps->t1_pfa)
2112 t1_check_block_len (mp, false);
2113 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2114 edecrypt (mp, t1_getbyte (mp));
2115 *mp->ps->t1_line_ptr++ = 0;
2117 mp->ps->t1_eexec_encrypt = true;
2118 if (!mp->ps->read_encoding_only)
2119 if (is_included (fm_cur))
2120 t1_putline (mp); /* to put the first four bytes */
2122 static void t1_stop_eexec (MP mp) {
2124 end_last_eexec_line ();
2125 if (!mp->ps->t1_pfa)
2126 t1_check_block_len (mp,true);
2128 c = edecrypt (mp, t1_getbyte (mp));
2129 if (!(c == 10 || c == 13)) {
2130 if (mp->ps->last_hexbyte == 0)
2133 mp_warn (mp,"unexpected data after eexec");
2136 mp->ps->t1_cs = false;
2137 mp->ps->t1_in_eexec = 2;
2139 static void t1_modify_fm (MP mp) {
2140 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2143 static void t1_modify_italic (MP mp) {
2144 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2149 const char *pdfname;
2159 static key_entry font_keys[FONT_KEYS_NUM] = {
2160 {"Ascent", "Ascender", 0, false},
2161 {"CapHeight", "CapHeight", 0, false},
2162 {"Descent", "Descender", 0, false},
2163 {"FontName", "FontName", 0, false},
2164 {"ItalicAngle", "ItalicAngle", 0, false},
2165 {"StemV", "StdVW", 0, false},
2166 {"XHeight", "XHeight", 0, false},
2167 {"FontBBox", "FontBBox", 0, false},
2179 @d ITALIC_ANGLE_CODE 4
2185 @d FONTBBOX4_CODE 10
2186 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2189 static void t1_scan_keys (MP mp, int tex_font,fm_entry *fm_cur) {
2193 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2194 if (t1_prefix ("/FontMatrix")) {
2198 if (t1_prefix ("/ItalicAngle")) {
2199 t1_modify_italic (mp);
2203 if (t1_prefix ("/FontType")) {
2204 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2205 if ((i = t1_scan_num (mp,p, 0)) != 1) {
2207 mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2208 mp_fatal_error(mp,s);
2212 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2213 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2215 if (key - font_keys == MAX_KEY_CODE)
2218 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2220 if ((k = key - font_keys) == FONTNAME_CODE) {
2223 remove_eol (p, mp->ps->t1_line_array);
2224 mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2225 mp_fatal_error(mp,s);
2227 r = ++p; /* skip the slash */
2228 if (is_included (fm_cur)) {
2229 /* save the fontname */
2230 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2231 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2232 mp->ps->fontname_buf[i]=0;
2234 if(is_subsetted (fm_cur)) {
2235 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2236 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2238 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2240 alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
2242 strncpy (r, fm_cur->subset_tag , 6);
2244 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2245 mp->ps->t1_line_ptr = eol (r);
2247 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2249 mp->ps->t1_line_ptr = eol (r);
2254 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2255 && (*p == '[' || *p == '{'))
2257 if (k == FONTBBOX1_CODE) {
2258 for (i = 0; i < 4; i++) {
2259 key[i].value = t1_scan_num (mp, p, &r);
2264 key->value = t1_scan_num (mp, p, 0);
2266 static void t1_scan_param (MP mp, int tex_font,fm_entry *fm_cur)
2268 static const char *lenIV = "/lenIV";
2269 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2271 if (t1_prefix (lenIV)) {
2272 mp->ps->t1_lenIV = t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2275 t1_scan_keys (mp, tex_font,fm_cur);
2277 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2278 if (glyph_names[b] != notdef) {
2279 mp_xfree (glyph_names[b]);
2280 glyph_names[b] = (char *) notdef;
2282 if (glyph_names[a] != notdef) {
2283 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2286 static void t1_builtin_enc (MP mp) {
2287 int i, a, b, c, counter = 0;
2290 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2292 if (t1_suffix ("def")) { /* predefined encoding */
2293 sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2294 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2295 for (i = 0; i < 256; i++)
2296 if (standard_glyph_names[i] == notdef)
2297 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2299 mp->ps->t1_builtin_glyph_names[i] =
2300 mp_xstrdup (mp,standard_glyph_names[i]);
2301 mp->ps->t1_encoding = ENC_STANDARD;
2304 mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2305 mp->ps->t1_buf_array);
2306 mp_fatal_error(mp,s);
2310 mp->ps->t1_encoding = ENC_BUILTIN;
2312 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2313 * not a predefined encoding
2315 * We have two possible forms of Encoding vector. The first case is
2317 * /Encoding [/a /b /c...] readonly def
2319 * and the second case can look like
2321 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2327 for (i = 0; i < 256; i++)
2328 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2329 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2330 r = strchr (mp->ps->t1_line_array, '[') + 1;
2334 for (p = mp->ps->t1_buf_array, r++;
2335 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2339 if (counter > 255) {
2341 (mp, "encoding vector contains more than 256 names");
2343 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2344 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2347 if (*r != 10 && *r != '%') {
2348 if (str_prefix (r, "] def")
2349 || str_prefix (r, "] readonly def"))
2353 remove_eol (r, mp->ps->t1_line_array);
2354 mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2355 mp->ps->t1_line_array);
2356 mp_fatal_error(mp,s);
2360 r = mp->ps->t1_line_array;
2362 } else { /* the second case */
2363 p = strchr (mp->ps->t1_line_array, 10);
2367 p = mp->ps->t1_line_array;
2370 check for `dup <index> <glyph> put'
2372 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2373 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2374 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2375 mp->ps->t1_builtin_glyph_names[i] =
2376 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2377 p = strstr (p, " put") + strlen (" put");
2381 check for `dup dup <to> exch <from> get put'
2383 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2384 && valid_code (a) && valid_code (b)) {
2385 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2386 p = strstr (p, " get put") + strlen (" get put");
2390 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2393 (p, "dup dup %i %i getinterval %i exch putinterval",
2394 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2395 && valid_code (c)) {
2396 for (i = 0; i < c; i++)
2397 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2398 p = strstr (p, " putinterval") + strlen (" putinterval");
2402 check for `def' or `readonly def'
2404 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2405 && strcmp (p, "def\n") == 0)
2408 skip an unrecognizable word
2411 while (*p != ' ' && *p != 10)
2419 static void t1_check_end (MP mp) {
2423 if (t1_prefix ("{restore}"))
2429 char *ff_name; /* base name of font file */
2430 char *ff_path; /* full path to font file */
2434 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2436 ff = check_ff_exist (mp, fm_cur);
2437 if (ff->ff_path != NULL) {
2438 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font);
2440 mp_warn (mp, "cannot open Type 1 font file for reading");
2443 t1_init_params (mp,open_name_prefix,fm_cur->ff_name);
2444 mp->ps->fontfile_found = true;
2448 static void t1_scan_only (MP mp, int tex_font, fm_entry *fm_cur) {
2451 t1_scan_param (mp,tex_font, fm_cur);
2453 while (mp->ps->t1_in_eexec == 0);
2454 t1_start_eexec (mp,fm_cur);
2457 t1_scan_param (mp,tex_font, fm_cur);
2459 while (!(t1_charstrings () || t1_subrs ()));
2462 static void t1_include (MP mp, int tex_font, fm_entry *fm_cur) {
2465 t1_scan_param (mp,tex_font, fm_cur);
2468 while (mp->ps->t1_in_eexec == 0);
2469 t1_start_eexec (mp,fm_cur);
2472 t1_scan_param (mp,tex_font, fm_cur);
2475 while (!(t1_charstrings () || t1_subrs ()));
2476 mp->ps->t1_cs = true;
2481 while (!t1_end_eexec ());
2483 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2488 while (!t1_cleartomark ());
2489 t1_check_end (mp); /* write "{restore}if" if found */
2494 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2496 mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2497 mp_fatal_error(mp,s);
2501 static const char **check_cs_token_pair (MP mp) {
2502 const char **p = (const char **) cs_token_pairs_list;
2503 for (; p[0] != NULL; ++p)
2504 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2509 static void cs_store (MP mp, boolean is_subr) {
2513 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2514 *mp->ps->t1_buf_ptr++ = *p++);
2515 *mp->ps->t1_buf_ptr = 0;
2517 subr = t1_scan_num (mp, p + 1, 0);
2519 ptr = mp->ps->subr_tab + subr;
2521 ptr = mp->ps->cs_ptr++;
2522 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2524 mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2525 mp_fatal_error(mp,s);
2527 if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
2528 ptr->glyph_name = (char *) notdef;
2530 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2532 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2533 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2534 (unsigned) (mp->ps->t1_cslen + 4));
2535 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2536 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2537 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2538 *mp->ps->t1_buf_ptr++ = 10;
2539 if (is_subr && mp->ps->cs_token_pair == NULL)
2540 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2541 ptr->len = mp->ps->t1_buf_ptr - mp->ps->t1_buf_array;
2542 ptr->cslen = mp->ps->t1_cslen;
2543 ptr->data = mp_xmalloc (mp,ptr->len , sizeof (byte));
2544 memcpy (ptr->data, mp->ps->t1_buf_array, ptr->len);
2548 #define store_subr(mp) cs_store(mp,true)
2549 #define store_cs(mp) cs_store(mp,false)
2551 #define CC_STACK_SIZE 24
2553 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2554 static cc_entry cc_tab[CS_MAX];
2555 static boolean is_cc_init = false;
2559 if (stack_ptr - cc_stack < (N)) \
2563 #define stack_error(N) { \
2565 mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2566 (int) N, (int)(stack_ptr - cc_stack)); \
2572 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2574 #define cc_push(V) *stack_ptr++ = V
2575 #define cc_clear() stack_ptr = cc_stack
2577 #define set_cc(N, B, A, C) \
2578 cc_tab[N].nargs = A; \
2579 cc_tab[N].bottom = B; \
2580 cc_tab[N].clear = C; \
2581 cc_tab[N].valid = true
2583 static void cc_init (void) {
2587 for (i = 0; i < CS_MAX; i++)
2588 cc_tab[i].valid = false;
2589 set_cc (CS_HSTEM, true, 2, true);
2590 set_cc (CS_VSTEM, true, 2, true);
2591 set_cc (CS_VMOVETO, true, 1, true);
2592 set_cc (CS_RLINETO, true, 2, true);
2593 set_cc (CS_HLINETO, true, 1, true);
2594 set_cc (CS_VLINETO, true, 1, true);
2595 set_cc (CS_RRCURVETO, true, 6, true);
2596 set_cc (CS_CLOSEPATH, false, 0, true);
2597 set_cc (CS_CALLSUBR, false, 1, false);
2598 set_cc (CS_RETURN, false, 0, false);
2600 |set_cc(CS_ESCAPE, false, 0, false);|
2602 set_cc (CS_HSBW, true, 2, true);
2603 set_cc (CS_ENDCHAR, false, 0, true);
2604 set_cc (CS_RMOVETO, true, 2, true);
2605 set_cc (CS_HMOVETO, true, 1, true);
2606 set_cc (CS_VHCURVETO, true, 4, true);
2607 set_cc (CS_HVCURVETO, true, 4, true);
2608 set_cc (CS_DOTSECTION, false, 0, true);
2609 set_cc (CS_VSTEM3, true, 6, true);
2610 set_cc (CS_HSTEM3, true, 6, true);
2611 set_cc (CS_SEAC, true, 5, true);
2612 set_cc (CS_SBW, true, 4, true);
2613 set_cc (CS_DIV, false, 2, false);
2614 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2615 set_cc (CS_POP, false, 0, false);
2616 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2622 @d cs_getchar(mp) cdecrypt(*data++, &cr)
2624 @d mark_subr(mp,n) cs_mark(mp,0, n)
2625 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2626 @d SMALL_BUF_SIZE 256
2629 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2630 char buf[SMALL_BUF_SIZE];
2633 va_start (args, fmt);
2634 vsprintf (buf, fmt, args);
2636 if (cs_name == NULL) {
2637 mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2639 mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2644 static void cs_mark (MP mp, const char *cs_name, int subr)
2650 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2654 if (cs_name == NULL) {
2656 ptr = mp->ps->subr_tab + subr;
2660 if (mp->ps->cs_notdef != NULL &&
2661 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2662 ptr = mp->ps->cs_notdef;
2664 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2665 if (strcmp (ptr->glyph_name, cs_name) == 0)
2667 if (ptr == mp->ps->cs_ptr) {
2669 mp_snprintf (s,128,"glyph `%s' undefined", cs_name);
2673 if (ptr->glyph_name == notdef)
2674 mp->ps->cs_notdef = ptr;
2677 /* only marked CharString entries and invalid entries can be skipped;
2678 valid marked subrs must be parsed to keep the stack in sync */
2679 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2681 ptr->is_used = true;
2683 cs_len = ptr->cslen;
2684 data = ptr->data + 4;
2685 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2687 while (cs_len > 0) {
2689 b = cs_getchar (mp);
2693 else if (b <= 250) {
2695 a = ((b - 247) << 8) + 108 + cs_getchar (mp);
2696 } else if (b <= 254) {
2698 a = -((b - 251) << 8) - 108 - cs_getchar (mp);
2701 a = (cs_getchar (mp) & 0xff) << 24;
2702 a |= (cs_getchar (mp) & 0xff) << 16;
2703 a |= (cs_getchar (mp) & 0xff) << 8;
2704 a |= (cs_getchar (mp) & 0xff) << 0;
2705 if (sizeof (integer) > 4 && (a & 0x80000000))
2710 if (b == CS_ESCAPE) {
2711 b = cs_getchar (mp) + CS_1BYTE_MAX;
2715 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2721 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2725 if (stack_ptr - cc_stack < cc->nargs)
2726 cs_warn (mp,cs_name, subr,
2727 "less arguments on stack (%i) than required (%i)",
2728 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2729 else if (stack_ptr - cc_stack > cc->nargs)
2730 cs_warn (mp,cs_name, subr,
2731 "more arguments on stack (%i) than required (%i)",
2732 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2734 switch (cc - cc_tab) {
2739 if (!mp->ps->subr_tab[a1].valid) {
2740 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2748 case CS_CALLOTHERSUBR:
2749 if (cc_get (-1) == 3)
2750 lastargOtherSubr3 = cc_get (-3);
2751 a1 = cc_get (-2) + 2;
2755 cc_push (lastargOtherSubr3);
2756 /* the only case when we care about the value being pushed onto
2757 stack is when POP follows CALLOTHERSUBR (changing hints by
2765 mark_cs (mp,standard_glyph_names[a1]);
2766 mark_cs (mp,standard_glyph_names[a2]);
2775 cs_error: /* an error occured during parsing */
2778 ptr->is_used = false;
2781 static void t1_subset_ascii_part (MP mp, int tex_font, fm_entry *fm_cur)
2785 while (!t1_prefix ("/Encoding")) {
2786 t1_scan_param (mp,tex_font, fm_cur);
2790 t1_builtin_enc (mp);
2791 if (is_reencoded (fm_cur))
2792 mp->ps->t1_glyph_names = external_enc ();
2794 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2796 |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2797 make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2798 update_subset_tag ();
2801 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2802 t1_puts (mp,"/Encoding StandardEncoding def\n");
2805 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2806 for (i = 0, j = 0; i < 256; i++) {
2807 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2809 t1_printf (mp,"dup %i /%s put\n", (int) t1_char (i),
2810 mp->ps->t1_glyph_names[i]);
2813 /* We didn't mark anything for the Encoding array. */
2814 /* We add "dup 0 /.notdef put" for compatibility */
2815 /* with Acrobat 5.0. */
2817 t1_puts (mp,"dup 0 /.notdef put\n");
2818 t1_puts (mp,"readonly def\n");
2822 t1_scan_param (mp,tex_font, fm_cur);
2823 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
2826 while (mp->ps->t1_in_eexec == 0);
2829 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
2830 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
2832 static void cs_init (MP mp) {
2833 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2834 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2835 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2836 mp->ps->cs_token_pair = NULL;
2837 mp->ps->subr_tab = NULL;
2838 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2839 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2842 static void init_cs_entry ( cs_entry * cs) {
2844 cs->glyph_name = NULL;
2847 cs->is_used = false;
2851 static void t1_mark_glyphs (MP mp, int tex_font);
2853 static void t1_read_subrs (MP mp, int tex_font, fm_entry *fm_cur)
2858 while (!(t1_charstrings () || t1_subrs ())) {
2859 t1_scan_param (mp,tex_font, fm_cur);
2864 mp->ps->t1_cs = true;
2865 mp->ps->t1_scan = false;
2868 mp->ps->subr_size_pos = strlen ("/Subrs") + 1;
2869 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2870 mp->ps->subr_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2871 if (mp->ps->subr_size == 0) {
2872 while (!t1_charstrings ())
2876 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
2877 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,mp->ps->subr_size, sizeof (cs_entry));
2878 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2879 init_cs_entry (ptr);
2880 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2882 while (mp->ps->t1_cslen) {
2886 /* mark the first four entries without parsing */
2887 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2888 mp->ps->subr_tab[i].is_used = true;
2889 /* the end of the Subrs array might have more than one line so we need to
2890 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2891 the Subrs array followed by the CharStrings dict immediately (synthetic
2892 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2893 we will treat the font as synthetic and ignore everything until next
2896 #define POST_SUBRS_SCAN 5
2898 *mp->ps->t1_buf_array = 0;
2899 for (i = 0; i < POST_SUBRS_SCAN; i++) {
2900 if (t1_charstrings ())
2902 s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2903 alloc_array (t1_buf, s, T1_BUF_SIZE);
2904 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2907 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2908 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2909 suppose synthetic font */
2910 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2912 mp_xfree (ptr->data);
2913 mp_xfree (mp->ps->subr_tab);
2914 mp_xfree (mp->ps->subr_array_start);
2915 mp_xfree (mp->ps->subr_array_end);
2917 mp->ps->t1_cs = false;
2918 mp->ps->t1_synthetic = true;
2919 while (!(t1_charstrings () || t1_subrs ()))
2926 static void t1_flush_cs (MP mp, boolean is_subr)
2929 byte *r, *return_cs = NULL;
2930 cs_entry *tab, *end_tab, *ptr;
2931 char *start_line, *line_end;
2932 int count, size_pos;
2933 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2935 start_line = mp->ps->subr_array_start;
2936 line_end = mp->ps->subr_array_end;
2937 size_pos = mp->ps->subr_size_pos;
2938 tab = mp->ps->subr_tab;
2939 count = mp->ps->subr_max + 1;
2940 end_tab = mp->ps->subr_tab + count;
2942 start_line = mp->ps->cs_dict_start;
2943 line_end = mp->ps->cs_dict_end;
2944 size_pos = mp->ps->cs_size_pos;
2945 tab = mp->ps->cs_tab;
2946 end_tab = mp->ps->cs_ptr;
2947 count = mp->ps->cs_count;
2949 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2950 for (p = start_line; p - start_line < size_pos;)
2951 *mp->ps->t1_line_ptr++ = *p++;
2952 while (isdigit (*p))
2954 sprintf (mp->ps->t1_line_ptr, "%u", count);
2955 strcat (mp->ps->t1_line_ptr, p);
2956 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2959 /* create |return_cs| to replace unsused subr's */
2963 return_cs = mp_xmalloc (mp, (mp->ps->t1_lenIV + 1) , sizeof(byte));
2964 if ( mp->ps->t1_lenIV > 0) {
2965 for (cs_len = 0, r = return_cs; cs_len < mp->ps->t1_lenIV; cs_len++, r++)
2966 *r = cencrypt (0x00, &cr);
2967 *r = cencrypt (CS_RETURN, &cr);
2969 *return_cs = CS_RETURN;
2974 for (ptr = tab; ptr < end_tab; ptr++) {
2977 sprintf (mp->ps->t1_line_array, "dup %i %u", (int) (ptr - tab),
2980 sprintf (mp->ps->t1_line_array, "/%s %u", ptr->glyph_name, ptr->cslen);
2981 p = strend (mp->ps->t1_line_array);
2982 memcpy (p, ptr->data, ptr->len);
2983 mp->ps->t1_line_ptr = p + ptr->len;
2986 /* replace unsused subr's by |return_cs| */
2988 sprintf (mp->ps->t1_line_array, "dup %i %u%s ", (int) (ptr - tab),
2989 cs_len, mp->ps->cs_token_pair[0]);
2990 p = strend (mp->ps->t1_line_array);
2991 memcpy (p, return_cs, cs_len);
2992 mp->ps->t1_line_ptr = p + cs_len;
2994 sprintf (mp->ps->t1_line_array, " %s", mp->ps->cs_token_pair[1]);
2995 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2999 mp_xfree (ptr->data);
3000 if (ptr->glyph_name != notdef)
3001 mp_xfree (ptr->glyph_name);
3003 sprintf (mp->ps->t1_line_array, "%s", line_end);
3004 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3007 mp_xfree (return_cs);
3009 mp_xfree (start_line);
3010 mp_xfree (line_end);
3013 static void t1_mark_glyphs (MP mp, int tex_font)
3016 char *charset = extra_charset ();
3019 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
3020 if (mp->ps->cs_tab != NULL)
3021 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3023 ptr->is_used = true;
3024 if (mp->ps->subr_tab != NULL) {
3025 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3027 ptr->is_used = true;
3028 mp->ps->subr_max = mp->ps->subr_size - 1;
3032 mark_cs (mp,notdef);
3033 for (i = 0; i < 256; i++)
3034 if (is_used_char (i)) {
3035 if (mp->ps->t1_glyph_names[i] == notdef) {
3037 mp_snprintf(S,128, "character %i is mapped to %s", i, notdef);
3040 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3042 if (charset == NULL)
3044 g = s = charset + 1; /* skip the first '/' */
3047 while (*s != '/' && s < r)
3049 *s = 0; /* terminate g by rewriting '/' to 0 */
3054 if (mp->ps->subr_tab != NULL)
3055 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3056 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3058 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3059 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3062 static void t1_subset_charstrings (MP mp, int tex_font)
3065 mp->ps->cs_size_pos =
3066 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3067 - mp->ps->t1_line_array + 1;
3068 /* |cs_size_pos| points to the number indicating
3069 dict size after "/CharStrings" */
3070 mp->ps->cs_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3071 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,mp->ps->cs_size, sizeof(cs_entry));
3072 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3073 init_cs_entry (ptr);
3074 mp->ps->cs_notdef = NULL;
3075 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3077 while (mp->ps->t1_cslen) {
3081 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3082 t1_mark_glyphs (mp,tex_font);
3083 if (mp->ps->subr_tab != NULL) {
3084 if (mp->ps->cs_token_pair == NULL)
3086 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3089 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3095 static void t1_subset_end (MP mp)
3097 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3098 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3102 while (!t1_end_eexec ())
3103 t1_getline (mp); /* ignore the rest */
3104 t1_putline (mp); /* write "mark currentfile closefile" */
3106 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3111 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3112 while (!t1_cleartomark ()) {
3116 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3117 t1_check_end (mp); /* write "{restore}if" if found */
3121 static int t1_updatefm (MP mp, int f, fm_entry *fm)
3124 mp->ps->read_encoding_only = true;
3125 if (!t1_open_fontfile (mp,fm,NULL)) {
3128 t1_scan_only (mp,f, fm);
3129 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3131 while (*p != ' ' && *p != 0)
3135 t1_close_font_file (mp,"");
3140 static void writet1 (MP mp, int tex_font, fm_entry *fm_cur) {
3141 int save_selector = mp->selector;
3142 mp_normalize_selector(mp);
3143 mp->ps->read_encoding_only = false;
3144 if (!is_included (fm_cur)) { /* scan parameters from font file */
3145 if (!t1_open_fontfile (mp,fm_cur,"{"))
3147 t1_scan_only (mp,tex_font, fm_cur);
3148 t1_close_font_file (mp,"}");
3151 if (!is_subsetted (fm_cur)) { /* include entire font */
3152 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3154 t1_include (mp,tex_font,fm_cur);
3155 t1_close_font_file (mp,">>");
3158 /* partial downloading */
3159 if (!t1_open_fontfile (mp,fm_cur,"<"))
3161 t1_subset_ascii_part (mp,tex_font,fm_cur);
3162 t1_start_eexec (mp,fm_cur);
3165 t1_read_subrs (mp,tex_font, fm_cur);
3166 t1_subset_charstrings (mp,tex_font);
3168 t1_close_font_file (mp,">");
3169 mp->selector = save_selector;
3173 static void t1_free (MP mp);
3176 static void t1_free (MP mp) {
3177 mp_xfree (mp->ps->t1_line_array);
3178 mp_xfree (mp->ps->t1_buf_array);
3182 @* \[44d] Embedding fonts.
3184 @ The |tfm_num| is officially of type |font_number|, but that
3185 type does not exist yet at this point in the output order.
3189 char *tfm_name; /* TFM file name */
3190 char *ps_name; /* PostScript name */
3191 integer flags; /* font flags */
3192 char *ff_name; /* font file name */
3193 char *subset_tag; /* pseudoUniqueTag for subsetted font */
3194 enc_entry *encoding; /* pointer to corresponding encoding */
3195 unsigned int tfm_num; /* number of the TFM refering this entry */
3196 unsigned short type; /* font type (T1/TTF/...) */
3197 short slant; /* SlantFont */
3198 short extend; /* ExtendFont */
3199 integer ff_objnum; /* FontFile object number */
3200 integer fn_objnum; /* FontName/BaseName object number */
3201 integer fd_objnum; /* FontDescriptor object number */
3202 char *charset; /* string containing used glyphs */
3203 boolean all_glyphs; /* embed all glyphs? */
3204 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
3205 short tfm_avail; /* flags whether a tfm is available */
3206 short pid; /* Pid for truetype fonts */
3207 short eid; /* Eid for truetype fonts */
3213 #define FONTNAME_BUF_SIZE 128
3214 boolean fontfile_found;
3215 boolean is_otf_font;
3216 char fontname_buf[FONTNAME_BUF_SIZE];
3224 @d set_included(fm) ((fm)->type |= F_INCLUDED)
3225 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
3226 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
3227 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
3229 @d is_included(fm) ((fm)->type & F_INCLUDED)
3230 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
3231 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
3232 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
3233 @d is_reencoded(fm) ((fm)->encoding != NULL)
3234 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
3235 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
3237 @d fm_slant(fm) (fm)->slant
3238 @d fm_extend(fm) (fm)->extend
3239 @d fm_fontfile(fm) (fm)->ff_name
3242 static boolean mp_font_is_reencoded (MP mp, int f);
3243 static boolean mp_font_is_included (MP mp, int f);
3244 static boolean mp_font_is_subsetted (MP mp, int f);
3247 static boolean mp_font_is_reencoded (MP mp, int f) {
3249 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3251 && (fm->ps_name != NULL)
3252 && is_reencoded (fm))
3257 static boolean mp_font_is_included (MP mp, int f) {
3259 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3261 && (fm->ps_name != NULL && fm->ff_name != NULL)
3262 && is_included (fm))
3267 static boolean mp_font_is_subsetted (MP mp, int f) {
3269 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
3271 && (fm->ps_name != NULL && fm->ff_name != NULL)
3272 && is_included (fm) && is_subsetted (fm))
3278 @ @<Exported function headers@>=
3279 char * mp_fm_encoding_name (MP mp, int f);
3280 char * mp_fm_font_name (MP mp, int f);
3283 static char * mp_fm_font_subset_name (MP mp, int f);
3286 @c char * mp_fm_encoding_name (MP mp, int f) {
3289 if (mp_has_fm_entry (mp, f, &fm)) {
3290 if (fm != NULL && (fm->ps_name != NULL)) {
3291 if (is_reencoded (fm)) {
3293 if (e->enc_name!=NULL)
3294 return mp_xstrdup(mp,e->enc_name);
3300 print_err ("fontmap encoding problems for font ");
3301 mp_print(mp,mp->font_name[f]);
3305 char * mp_fm_font_name (MP mp, int f) {
3307 if (mp_has_fm_entry (mp, f,&fm)) {
3308 if (fm != NULL && (fm->ps_name != NULL)) {
3309 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3310 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3311 if (t1_updatefm(mp,f,fm)) {
3312 mp->font_ps_name_fixed[f] = true;
3314 print_err ("font loading problems for font ");
3315 mp_print(mp,mp->font_name[f]);
3319 return mp_xstrdup(mp,fm->ps_name);
3322 print_err ("fontmap name problems for font ");
3323 mp_print(mp,mp->font_name[f]);
3328 static char * mp_fm_font_subset_name (MP mp, int f) {
3330 if (mp_has_fm_entry (mp, f, &fm)) {
3331 if (fm != NULL && (fm->ps_name != NULL)) {
3332 if (is_subsetted(fm)) {
3333 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3334 mp_snprintf(s,strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3337 return mp_xstrdup(mp,fm->ps_name);
3341 print_err ("fontmap name problems for font ");
3342 mp_print(mp,mp->font_name[f]);
3348 static integer mp_fm_font_slant (MP mp, int f);
3349 static integer mp_fm_font_extend (MP mp, int f);
3352 @c static integer mp_fm_font_slant (MP mp, int f) {
3354 if (mp_has_fm_entry (mp, f, &fm)) {
3355 if (fm != NULL && (fm->ps_name != NULL)) {
3361 static integer mp_fm_font_extend (MP mp, int f) {
3363 if (mp_has_fm_entry (mp, f, &fm)) {
3364 if (fm != NULL && (fm->ps_name != NULL)) {
3372 static boolean mp_do_ps_font (MP mp, font_number f);
3374 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3376 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3379 if (is_truetype(fm_cur) ||
3380 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3383 if (is_included(fm_cur)) {
3384 mp_ps_print_nl(mp,"%%BeginResource: font ");
3385 if (is_subsetted(fm_cur)) {
3386 mp_ps_print(mp, fm_cur->subset_tag);
3387 mp_ps_print_char(mp,'-');
3389 mp_ps_print(mp, fm_cur->ps_name);
3391 writet1 (mp,f,fm_cur);
3392 mp_ps_print_nl(mp,"%%EndResource");
3398 @ Included subset fonts do not need and encoding vector, make
3399 sure we skip that case.
3402 static void mp_list_used_resources (MP mp, int prologues, int procset);
3404 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
3405 font_number f; /* fonts used in a text node or as loop counters */
3406 int ff; /* a loop counter */
3407 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3410 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
3412 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3415 for (f=null_font+1;f<=mp->last_fnum;f++) {
3416 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3417 for (ff=ldf;ff>=null_font;ff--) {
3418 if ( mp_has_font_size(mp,ff) )
3419 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3422 if ( mp_font_is_subsetted(mp,f) )
3424 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
3425 (unsigned)mp->max_print_line )
3426 mp_ps_print_nl(mp, "%%+ encoding");
3429 mp_ps_print_nl(mp, "%%+ encoding");
3431 mp_ps_print_char(mp, ' ');
3432 mp_ps_print(mp, mp->font_enc_name[f]);
3440 for (f=null_font+1;f<=mp->last_fnum;f++) {
3441 if ( mp_has_font_size(mp,f) ) {
3442 for (ff=ldf;ff>=null_font;ff--) {
3443 if ( mp_has_font_size(mp,ff) )
3444 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3447 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
3448 (unsigned)mp->max_print_line )
3449 mp_ps_print_nl(mp, "%%+ font");
3452 mp_ps_print_nl(mp, "%%+ font");
3454 mp_ps_print_char(mp, ' ');
3455 if ( (prologues==3)&&
3456 (mp_font_is_subsetted(mp,f)) )
3457 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3459 mp_ps_print(mp, mp->font_ps_name[f]);
3469 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
3471 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
3472 font_number f; /* fonts used in a text node or as loop counters */
3473 int ff; /* a loop counter */
3474 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3477 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3479 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3482 for (f=null_font+1;f<=mp->last_fnum;f++) {
3483 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3484 for (ff=ldf;ff>= null_font;ff++) {
3485 if ( mp_has_font_size(mp,ff) )
3486 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3489 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3491 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(unsigned)mp->max_print_line )
3492 mp_ps_print_nl(mp, "%%+ encoding");
3495 mp_ps_print_nl(mp, "%%+ encoding");
3497 mp_ps_print_char(mp, ' ');
3498 mp_ps_print(mp, mp->font_enc_name[f]);
3507 for (f=null_font+1;f<=mp->last_fnum;f++) {
3508 if ( mp_has_font_size(mp,f) ) {
3509 for (ff=ldf;ff>= null_font;ff--) {
3510 if ( mp_has_font_size(mp,ff) )
3511 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3514 if ( ! mp_font_is_included(mp,f) )
3516 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3517 mp_ps_print_nl(mp, "%%+ font");
3520 mp_ps_print_nl(mp, "%%+ font");
3522 mp_ps_print_char(mp, ' ');
3523 if ( mp_font_is_subsetted(mp,f) )
3524 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3526 mp_ps_print(mp, mp->font_ps_name[f]);
3536 @ @<Declarations...@>=
3537 static void mp_list_needed_resources (MP mp, int prologues);
3539 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
3540 font_number f; /* fonts used in a text node or as loop counters */
3541 int ff; /* a loop counter */
3542 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3546 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3547 if ( mp_has_font_size(mp,f)) {
3548 for (ff=ldf;ff>=null_font;ff--) {
3549 if ( mp_has_font_size(mp,ff) )
3550 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3553 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3555 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3556 mp_ps_print_nl(mp, "%%+ font");
3559 mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
3561 mp_ps_print_char(mp, ' ');
3562 mp_ps_print(mp, mp->font_ps_name[f]);
3568 if ( ! firstitem ) {
3572 for (f=null_font+1;f<= mp->last_fnum;f++) {
3573 if ( mp_has_font_size(mp,f) ) {
3574 for (ff=ldf;ff>=null_font;ff-- ) {
3575 if ( mp_has_font_size(mp,ff) )
3576 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3579 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3581 mp_ps_print(mp, "%%IncludeResource: font ");
3582 mp_ps_print(mp, mp->font_ps_name[f]);
3593 static void mp_write_font_definition (MP mp, font_number f, int prologues);
3597 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3598 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3600 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
3601 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3602 (mp_fm_font_extend(mp,f)!=0)||
3603 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3604 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3605 if ( (mp_font_is_subsetted(mp,f))&&
3606 (mp_font_is_included(mp,f))&&(prologues==3))
3607 mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3609 mp_ps_name_out(mp, mp->font_ps_name[f],true);
3610 mp_ps_print(mp, " fcp");
3612 if ( applied_reencoding(f) ) {
3613 mp_ps_print(mp, "/Encoding ");
3614 mp_ps_print(mp, mp->font_enc_name[f]);
3615 mp_ps_print(mp, " def ");
3617 if ( mp_fm_font_slant(mp,f)!=0 ) {
3618 mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
3619 mp_ps_print(mp, " SlantFont ");
3621 if ( mp_fm_font_extend(mp,f)!=0 ) {
3622 mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3623 mp_ps_print(mp, " ExtendFont ");
3625 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3626 mp_ps_print(mp, " 890 ScaleFont ");
3627 mp_ps_print(mp, " 277 SlantFont ");
3629 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3630 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3631 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3632 mp_ps_print(mp, "/space[0 -278]def ");
3633 mp_ps_print(mp, "/a12[-904 -939]def ");
3634 mp_ps_print(mp, "end def ");
3636 mp_ps_print(mp, "currentdict end");
3638 mp_ps_print_defined_name(mp,f,prologues);
3639 mp_ps_print(mp, " exch definefont pop");
3645 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
3648 @c static void mp_ps_print_defined_name(MP mp, font_number A, int prologues) {
3649 mp_ps_print(mp, " /");
3650 if ((mp_font_is_subsetted(mp,(A)))&&
3651 (mp_font_is_included(mp,(A)))&&(prologues==3))
3652 mp_ps_print(mp, mp_fm_font_subset_name(mp,(A)));
3654 mp_ps_print(mp, mp->font_ps_name[(A)]);
3655 if ( mp_xstrcmp(mp->font_name[(A)],"psyrgo")==0 )
3656 mp_ps_print(mp, "-Slanted");
3657 if ( mp_xstrcmp(mp->font_name[(A)],"zpzdr-reversed")==0 )
3658 mp_ps_print(mp, "-Reverse");
3659 if ( applied_reencoding((A)) ) {
3660 mp_ps_print(mp, "-");
3661 mp_ps_print(mp, mp->font_enc_name[(A)]);
3663 if ( mp_fm_font_slant(mp,(A))!=0 ) {
3664 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,(A))) ;
3666 if ( mp_fm_font_extend(mp,(A))!=0 ) {
3667 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,(A)));
3671 @ @<Include encodings and fonts for edge structure~|h|@>=
3672 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
3673 @<Embed fonts that are available@>
3675 @ @<Embed fonts that are available@>=
3678 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3681 for (f=null_font+1;f<=mp->last_fnum;f++) {
3682 if ( cur_fsize[f]!=null ) {
3683 if (prologues==3 ) {
3684 if ( ! mp_do_ps_font(mp,f) ) {
3685 if ( mp_has_fm_entry(mp,f, NULL) ) {
3686 print_err("Font embedding failed");
3691 cur_fsize[f]=link(cur_fsize[f]);
3692 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3696 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3698 } while (! done_fonts);
3701 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3704 mp_apply_mark_string_chars(mp, h, next_size);
3707 @ We also need to keep track of which characters are used in text nodes
3708 in the edge structure that is being shipped out. This is done by procedures
3709 that use the left-over |b3| field in the |char_info| words; i.e.,
3710 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3713 enum mp_char_mark_state {mp_unused=0, mp_used};
3716 void mp_mark_string_chars (MP mp,font_number f, char *s) ;
3719 void mp_mark_string_chars (MP mp,font_number f, char *s) {
3720 integer b; /* |char_base[f]| */
3721 ASCII_code bc,ec; /* only characters between these bounds are marked */
3722 char *k; /* an index into string |s| */
3728 if ( (*k>=bc)&&(*k<=ec) )
3729 mp->font_info[b+*k].qqqq.b3=mp_used;
3736 void mp_unmark_font (MP mp,font_number f) ;
3739 void mp_unmark_font (MP mp,font_number f) {
3740 int k; /* an index into |font_info| */
3741 for (k= mp->char_base[f]+mp->font_bc[f];
3742 k<=mp->char_base[f]+mp->font_ec[f];
3744 mp->font_info[k].qqqq.b3=mp_unused;
3749 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) ;
3752 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
3753 quarterword next_size; /* the size index for fonts being listed */
3754 pointer *cur_fsize; /* current positions in |font_sizes| */
3755 boolean done_fonts; /* have we finished listing the fonts in the header? */
3756 font_number f; /* a font number for loops */
3757 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3758 mp_list_used_resources(mp, prologues, procset);
3759 mp_list_supplied_resources(mp, prologues, procset);
3760 mp_list_needed_resources(mp, prologues);
3761 mp_ps_print_nl(mp, "%%EndComments");
3762 mp_ps_print_nl(mp, "%%BeginProlog");
3764 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
3766 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3767 mp_ps_print_nl(mp, "/bd{bind def}bind def"
3768 "/fshow {exch findfont exch scalefont setfont show}bd");
3769 if ( procset>0 ) @<Print the procset@>;
3770 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
3771 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3772 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3773 "/fmd{/FontMatrix exch def}bd");
3774 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3775 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3776 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3777 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3778 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3779 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3780 mp_ps_print_nl(mp, "%%EndResource");
3781 @<Include encodings and fonts for edge structure~|h|@>;
3782 mp_ps_print_nl(mp, "%%EndProlog");
3783 mp_ps_print_nl(mp, "%%BeginSetup");
3785 for (f=null_font+1;f<=mp->last_fnum;f++) {
3786 if ( mp_has_font_size(mp,f) ) {
3787 if ( mp_has_fm_entry(mp,f,NULL) ) {
3788 mp_write_font_definition(mp,f, prologues);
3789 mp_ps_name_out(mp, mp->font_name[f],true);
3790 mp_ps_print_defined_name(mp,f, prologues);
3791 mp_ps_print(mp, " def");
3794 mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3796 mp_ps_name_out(mp, mp->font_name[f],true);
3797 mp_ps_name_out(mp, mp->font_name[f],true);
3798 mp_ps_print(mp, " def");
3803 mp_ps_print_nl(mp, "%%EndSetup");
3804 mp_ps_print_nl(mp, "%%Page: 1 1");
3806 mp_xfree(cur_fsize);
3810 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues);
3815 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) {
3816 quarterword next_size; /* the size index for fonts being listed */
3817 pointer *cur_fsize; /* current positions in |font_sizes| */
3818 int ff; /* a loop counter */
3819 boolean done_fonts; /* have we finished listing the fonts in the header? */
3820 font_number f; /* a font number for loops */
3821 scaled ds; /* design size and scale factor for a text node */
3822 font_number ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3823 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3824 if ( prologues>0 ) {
3825 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3826 |font_sizes| and eliminate duplicates@>;
3829 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3830 do { done_fonts=true;
3831 for (f=null_font+1;f<=mp->last_fnum;f++) {
3832 if ( cur_fsize[f]!=null ) {
3833 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3835 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
3837 if ( ! done_fonts ) {
3838 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3841 } while (! done_fonts);
3843 mp_xfree(cur_fsize);
3847 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3848 for (f=null_font+1;f<= mp->last_fnum;f++)
3849 cur_fsize[f]=mp->font_sizes[f]
3851 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3852 so we carefully remove duplicates. There is no harm in using a slow, brute-force
3855 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3858 for (f=null_font+1;f<= mp->last_fnum;f++) {
3859 if ( mp->font_sizes[f]!=null ) {
3860 if ( ldf==null_font )
3861 mp_ps_print_nl(mp, "%%DocumentFonts:");
3862 for (ff=ldf;ff>=null_font;ff--) {
3863 if ( mp->font_sizes[ff]!=null )
3864 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3867 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3868 mp_ps_print_nl(mp, "%%+");
3869 mp_ps_print_char(mp, ' ');
3870 mp_ps_print(mp, mp->font_ps_name[f]);
3879 static void mp_hex_digit_out (MP mp,small_number d) {
3880 if ( d<10 ) mp_ps_print_char(mp, d+'0');
3881 else mp_ps_print_char(mp, d+'a'-10);
3884 @ We output the marks as a hexadecimal bit string starting at |c| or
3885 |font_bc[f]|, whichever is greater. If the output has to be truncated
3886 to avoid exceeding |emergency_line_length| the return value says where to
3887 start scanning next time.
3890 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3893 @d emergency_line_length 255
3894 /* \ps\ output lines can be this long in unusual circumstances */
3897 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3898 eight_bits bc,ec; /* only encode characters between these bounds */
3899 integer lim; /* the maximum number of marks to encode before truncating */
3900 int p; /* |font_info| index for the current character */
3901 int d,b; /* used to construct a hexadecimal digit */
3902 lim=4*(emergency_line_length-mp->ps->ps_offset-4);
3906 @<Restrict the range |bc..ec| so that it contains no unused characters
3907 at either end and has length at most |lim|@>;
3908 @<Print the initial label indicating that the bitmap starts at |bc|@>;
3909 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3910 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3916 @ We could save time by setting the return value before the loop that
3917 decrements |ec|, but there is no point in being so tricky.
3919 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3920 p=mp->char_base[f]+bc;
3921 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3924 if ( ec>=bc+lim ) ec=bc+lim-1;
3925 p=mp->char_base[f]+ec;
3926 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3930 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3931 mp_ps_print_char(mp, ' ');
3932 mp_hex_digit_out(mp, bc / 16);
3933 mp_hex_digit_out(mp, bc % 16);
3934 mp_ps_print_char(mp, ':')
3938 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3940 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3942 mp_hex_digit_out(mp, d);
3945 if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
3948 mp_hex_digit_out(mp, d)
3951 @ Here is a simple function that determines whether there are any marked
3952 characters in font~|f| with character code at least~|c|.
3955 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
3958 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
3959 int p; /* |font_info| index for the current character */
3960 for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
3961 if ( mp->font_info[p].qqqq.b3==mp_used )
3968 @ If the file name is so long that it can't be printed without exceeding
3969 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
3970 line. We might have to repeat line in order to get the character usage
3971 information to fit within |emergency_line_length|.
3973 TODO: these two defines are also defined in mp.w!
3975 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
3976 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
3978 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
3980 while ( mp_check_ps_marks(mp, f,t) ) {
3981 mp_ps_print_nl(mp, "%*Font: ");
3982 if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
3984 mp_ps_print(mp, mp->font_name[f]);
3985 mp_ps_print_char(mp, ' ');
3986 ds=(mp->font_dsize[f] + 8) / 16;
3987 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
3988 if ( mp->ps->ps_offset+12>emergency_line_length ) break;
3989 mp_ps_print_char(mp, ' ');
3990 mp_ps_print_scaled(mp, ds);
3991 if ( mp->ps->ps_offset+5>emergency_line_length ) break;
3992 t=mp_ps_marks_out(mp, f,t);
3994 cur_fsize[f]=link(cur_fsize[f]);
3997 @ @<Print the procset@>=
3999 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
4000 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
4001 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
4002 "/p{closepath}bd/n{newpath}bd");
4003 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
4004 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
4005 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
4006 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
4007 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4011 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4012 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
4013 might not have reasonable |font_ps_name| entries, but we just charge ahead
4014 anyway. The user should not make \&{prologues} positive if this will cause
4016 @:prologues_}{\&{prologues} primitive@>
4019 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset);
4022 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
4025 ldf = mp_print_font_comments (mp, h, prologues);
4027 if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
4028 mp_read_psname_table(mp);
4029 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4030 if ( (prologues>0)||(procset>0) ) {
4031 if ( ldf!=null_font ) {
4032 if ( prologues>0 ) {
4033 for (f=null_font+1;f<=mp->last_fnum;f++) {
4034 if ( mp_has_font_size(mp,f) ) {
4035 mp_ps_name_out(mp, mp->font_name[f],true);
4036 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4037 mp_ps_print(mp, " def");
4042 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4048 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4049 if ( (prologues>0)&&(ldf!=null_font) )
4051 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4053 mp_ps_print_nl(mp, "/bd{bind def}bind def");
4054 @<Print the procset@>;
4055 mp_ps_print_nl(mp, "%%EndResource");
4059 mp_ps_print(mp, "%%EndProlog");
4060 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4063 @ \MP\ used to have one single routine to print to both `write' files
4064 and the PostScript output. Web2c redefines ``Character |k| cannot be
4065 printed'', and that resulted in some bugs where 8-bit characters were
4066 written to the PostScript file (reported by Wlodek Bzyl).
4068 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4069 a plain space, since that makes it easier to parse the result file
4072 @<Character |k| is not allowed in PostScript output@>=
4075 @ We often need to print a pair of coordinates.
4078 void mp_ps_pair_out (MP mp,scaled x, scaled y) {
4080 mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
4081 mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
4085 void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4088 void mp_ps_print_cmd (MP mp, const char *l, const char *s) {
4089 if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4090 else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4094 void mp_ps_print_cmd (MP mp, const char *l, const char *s) ;
4097 void mp_ps_string_out (MP mp, const char *s) {
4098 ASCII_code k; /* bits to be converted to octal */
4099 mp_ps_print(mp, "(");
4101 if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4102 mp_ps_print_char(mp, '\\');
4105 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4106 mp_ps_print_char(mp, '\\');
4107 mp_ps_print_char(mp, '0'+(k / 64));
4108 mp_ps_print_char(mp, '0'+((k / 8) % 8));
4109 mp_ps_print_char(mp, '0'+(k % 8));
4111 if ( (k=='(')||(k==')')||(k=='\\') )
4112 mp_ps_print_char(mp, '\\');
4113 mp_ps_print_char(mp, k);
4116 mp_ps_print_char(mp, ')');
4120 void mp_ps_string_out (MP mp, const char *s) ;
4122 @ This is a define because the function does not use its |mp| argument.
4124 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4127 static boolean mp_do_is_ps_name (char *s) {
4128 ASCII_code k; /* the character being checked */
4130 if ( (k<=' ')||(k>'~') ) return false;
4131 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4132 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4138 void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4141 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4142 ps_room(strlen(s)+2);
4143 mp_ps_print_char(mp, ' ');
4144 if ( mp_is_ps_name(mp, s) ) {
4145 if ( lit ) mp_ps_print_char(mp, '/');
4148 mp_ps_string_out(mp, s);
4149 if ( ! lit ) mp_ps_print(mp, "cvx ");
4150 mp_ps_print(mp, "cvn");
4155 @ These special comments described in the {\sl PostScript Language Reference
4156 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4157 We can't normally output ``conforming'' \ps\ because
4158 the structuring conventions don't allow us to say ``Please make sure the
4159 following characters are downloaded and define the \.{fshow} macro to access
4162 The exact bounding box is written out if |mp_prologues<0|, although this
4163 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4164 accurately. (Overfull boxes are avoided if an illustration is made to
4165 match a given \.{\char`\\hsize}.)
4168 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues);
4171 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) {
4173 mp_ps_print(mp, "%!PS");
4175 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
4176 mp_ps_print_nl(mp, "%%BoundingBox: ");
4177 if ( hh->_minx>hh->_maxx) {
4178 mp_ps_print(mp, "0 0 0 0");
4179 } else if ( prologues<0 ) {
4180 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4181 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4183 mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->_minx),mp_floor_scaled(mp, hh->_miny));
4184 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->_maxx),-mp_floor_scaled(mp, -hh->_maxy));
4186 mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
4187 if ( hh->_minx>hh->_maxx ) {
4188 mp_ps_print(mp, "0 0 0 0");
4190 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4191 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4193 mp_ps_print_nl(mp, "%%Creator: MetaPost ");
4194 mp_ps_print(mp, mp_metapost_version());
4195 mp_ps_print_nl(mp, "%%CreationDate: ");
4196 mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
4197 mp_ps_print_char(mp, '.');
4198 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month]));
4199 mp_ps_print_char(mp, '.');
4200 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
4201 mp_ps_print_char(mp, ':');
4202 t=mp_round_unscaled(mp, mp->internal[mp_time]);
4203 mp_ps_print_dd(mp, t / 60);
4204 mp_ps_print_dd(mp, t % 60);
4205 mp_ps_print_nl(mp, "%%Pages: 1");
4208 @ The most important output procedure is the one that gives the \ps\ version of
4212 typedef struct mp_knot {
4213 unsigned short left_type_field;
4214 unsigned short right_type_field;
4215 signed int x_coord_field;
4216 signed int y_coord_field;
4217 signed int left_x_field;
4218 signed int left_y_field;
4219 signed int right_x_field;
4220 signed int right_y_field;
4221 struct mp_knot * next_field;
4222 unsigned char originator_field;
4226 #define gr_left_type(A) (A)->left_type_field
4227 #define gr_right_type(A) (A)->right_type_field
4228 #define gr_x_coord(A) (A)->x_coord_field
4229 #define gr_y_coord(A) (A)->y_coord_field
4230 #define gr_left_x(A) (A)->left_x_field
4231 #define gr_left_y(A) (A)->left_y_field
4232 #define gr_right_x(A) (A)->right_x_field
4233 #define gr_right_y(A) (A)->right_y_field
4234 #define gr_next_knot(A) (A)->next_field
4235 #define gr_originator(A) (A)->originator_field
4238 mp_knot * mp_gr_insert_knot (MP mp, mp_knot *q, scaled x, scaled y) {
4239 /* returns the inserted knot */
4240 mp_knot *r; /* the new knot */
4241 r= mp_xmalloc(mp, 1, sizeof (mp_knot));
4242 gr_next_knot(r)=gr_next_knot(q); gr_next_knot(q)=r;
4243 gr_right_x(r)=gr_right_x(q);
4244 gr_right_y(r)=gr_right_y(q);
4247 gr_right_x(q)=gr_x_coord(q);
4248 gr_right_y(q)=gr_y_coord(q);
4249 gr_left_x(r)=gr_x_coord(r);
4250 gr_left_y(r)=gr_y_coord(r);
4251 gr_left_type(r)=mp_explicit;
4252 gr_right_type(r)=mp_explicit;
4253 gr_originator(r)=mp_program_code;
4258 @ If we want to duplicate a knot node, we can say |copy_knot|:
4261 mp_knot *mp_gr_copy_knot (MP mp, mp_knot *p) {
4262 mp_knot *q; /* the copy */
4263 q = mp_xmalloc(mp, 1, sizeof (mp_knot));
4264 memcpy(q,p,sizeof (mp_knot));
4265 gr_next_knot(q)=NULL;
4269 @ The |copy_path| routine makes a clone of a given path.
4272 mp_knot *mp_gr_copy_path (MP mp, mp_knot *p) {
4273 mp_knot *q, *pp, *qq; /* for list manipulation */
4276 q=mp_gr_copy_knot(mp, p);
4280 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4281 qq=gr_next_knot(qq);
4282 pp=gr_next_knot(pp);
4288 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4289 returns a pointer to the first node of the copy, if the path is a cycle,
4290 but to the final node of a non-cyclic copy. The global
4291 variable |path_tail| will point to the final node of the original path;
4292 this trick makes it easier to implement `\&{doublepath}'.
4294 All node types are assumed to be |endpoint| or |explicit| only.
4296 This function is currenly unused.
4299 mp_knot * mp_gr_htap_ypoc (MP mp, mp_knot *p) {
4300 mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4301 q=mp_xmalloc(mp, 1, sizeof (mp_knot)); /* this will correspond to |p| */
4304 gr_right_type(qq)=gr_left_type(pp);
4305 gr_left_type(qq)=gr_right_type(pp);
4306 gr_x_coord(qq)=gr_x_coord(pp);
4307 gr_y_coord(qq)=gr_y_coord(pp);
4308 gr_right_x(qq)=gr_left_x(pp);
4309 gr_right_y(qq)=gr_left_y(pp);
4310 gr_left_x(qq)=gr_right_x(pp);
4311 gr_left_y(qq)=gr_right_y(pp);
4312 gr_originator(qq)=gr_originator(pp);
4313 if ( gr_next_knot(pp)==p ) {
4315 /* |mp->path_tail=pp;| */ /* ? */
4318 rr=mp_xmalloc(mp, 1, sizeof (mp_knot));
4319 gr_next_knot(rr)=qq;
4321 pp=gr_next_knot(pp);
4325 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4326 calling the following subroutine.
4329 void mp_do_gr_toss_knot_list (mp_knot *p) ;
4332 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4335 void mp_do_gr_toss_knot_list (mp_knot * p) {
4336 mp_knot *q; /* the node being freed */
4337 mp_knot *r; /* the next node */
4350 void mp_gr_ps_path_out (MP mp, mp_knot *h) {
4351 mp_knot *p, *q; /* for scanning the path */
4352 scaled d; /* a temporary value */
4353 boolean curved; /* |true| unless the cubic is almost straight */
4355 mp_ps_print_cmd(mp, "newpath ","n ");
4356 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4357 mp_ps_print_cmd(mp, "moveto","m");
4360 if ( gr_right_type(p)==mp_endpoint ) {
4361 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4365 @<Start a new line and print the \ps\ commands for the curve from
4369 mp_ps_print_cmd(mp, " closepath"," p");
4372 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4374 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4377 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4378 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4379 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4380 mp_ps_print_cmd(mp, "curveto","c");
4382 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4383 mp_ps_print_cmd(mp, "lineto","l");
4386 @ Two types of straight lines come up often in \MP\ paths:
4387 cubics with zero initial and final velocity as created by |make_path| or
4388 |make_envelope|, and cubics with control points uniformly spaced on a line
4389 as created by |make_choices|.
4391 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4393 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4394 if ( gr_right_x(p)==gr_x_coord(p) )
4395 if ( gr_right_y(p)==gr_y_coord(p) )
4396 if ( gr_left_x(q)==gr_x_coord(q) )
4397 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4398 d=gr_left_x(q)-gr_right_x(p);
4399 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4400 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4401 { d=gr_left_y(q)-gr_right_y(p);
4402 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4403 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4406 @ The colored objects use a struct with anonymous fields to express the color parts:
4410 int _a_val, _b_val, _c_val, _d_val;
4413 @ The exported form of a dash pattern is simpler than the internal
4414 format, it is closely modelled to the PostScript model. The array of
4415 dashes is ended by a single negative value, because this is not
4416 allowed in PostScript.
4426 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
4429 void mp_do_gr_toss_dashes(mp_dash_object *dl);
4432 void mp_do_gr_toss_dashes(mp_dash_object *dl) {
4435 mp_xfree(dl->array_field);
4441 mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) {
4442 mp_dash_object *q = NULL;
4446 q = mp_xmalloc(mp, 1, sizeof (mp_dash_object));
4447 memcpy (q,dl,sizeof(mp_dash_object));
4448 if (dl->array_field != NULL) {
4450 while (*(dl->array_field+i) != -1) i++;
4451 q->array_field = mp_xmalloc(mp, i, sizeof (scaled));
4452 memcpy(q->array_field,dl->array_field, (i*sizeof(scaled)));
4458 @ Now for outputting the actual graphic objects. First, set up some
4459 structures and access macros.
4461 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4464 #define gr_type(A) (A)->_type_field
4465 #define gr_link(A) (A)->_link_field
4466 #define gr_color_model(A) (A)->color_model_field
4467 #define gr_red_val(A) (A)->color_field._a_val
4468 #define gr_green_val(A) (A)->color_field._b_val
4469 #define gr_blue_val(A) (A)->color_field._c_val
4470 #define gr_cyan_val(A) (A)->color_field._a_val
4471 #define gr_magenta_val(A) (A)->color_field._b_val
4472 #define gr_yellow_val(A) (A)->color_field._c_val
4473 #define gr_black_val(A) (A)->color_field._d_val
4474 #define gr_grey_val(A) (A)->color_field._a_val
4475 #define gr_path_p(A) (A)->path_p_field
4476 #define gr_htap_p(A) ((mp_fill_object *)A)->htap_p_field
4477 #define gr_pen_p(A) (A)->pen_p_field
4478 #define gr_ljoin_val(A) (A)->ljoin_field
4479 #define gr_lcap_val(A) ((mp_stroked_object *)A)->lcap_field
4480 #define gr_miterlim_val(A) (A)->miterlim_field
4481 #define gr_pre_script(A) (A)->pre_script_field
4482 #define gr_post_script(A) (A)->post_script_field
4483 #define gr_dash_p(A) ((mp_stroked_object *)A)->dash_p_field
4484 #define gr_name_type(A) ((mp_text_object *)A)->name_type_field
4485 #define gr_text_p(A) ((mp_text_object *)A)->text_p_field
4486 #define gr_font_n(A) ((mp_text_object *)A)->font_n_field
4487 #define gr_font_name(A) ((mp_text_object *)A)->font_name_field
4488 #define gr_font_dsize(A) ((mp_text_object *)A)->font_dsize_field
4489 #define gr_width_val(A) ((mp_text_object *)A)->width_field
4490 #define gr_height_val(A) ((mp_text_object *)A)->height_field
4491 #define gr_depth_val(A) ((mp_text_object *)A)->depth_field
4492 #define gr_tx_val(A) ((mp_text_object *)A)->tx_field
4493 #define gr_ty_val(A) ((mp_text_object *)A)->ty_field
4494 #define gr_txx_val(A) ((mp_text_object *)A)->txx_field
4495 #define gr_txy_val(A) ((mp_text_object *)A)->txy_field
4496 #define gr_tyx_val(A) ((mp_text_object *)A)->tyx_field
4497 #define gr_tyy_val(A) ((mp_text_object *)A)->tyy_field
4500 #define GRAPHIC_BODY \
4502 struct mp_graphic_object * _link_field
4504 typedef struct mp_graphic_object {
4506 } mp_graphic_object;
4508 typedef struct mp_text_object {
4510 char *pre_script_field;
4511 char *post_script_field;
4512 mp_color color_field;
4513 unsigned char color_model_field;
4514 unsigned char name_type_field;
4516 char *font_name_field ;
4517 unsigned int font_dsize_field ;
4518 unsigned int font_n_field ;
4530 typedef struct mp_fill_object {
4532 char *pre_script_field;
4533 char *post_script_field;
4534 mp_color color_field;
4535 unsigned char color_model_field;
4536 unsigned char ljoin_field ;
4537 mp_knot * path_p_field;
4538 mp_knot * htap_p_field;
4539 mp_knot * pen_p_field;
4540 int miterlim_field ;
4543 typedef struct mp_stroked_object {
4545 char *pre_script_field;
4546 char *post_script_field;
4547 mp_color color_field;
4548 unsigned char color_model_field;
4549 unsigned char ljoin_field ;
4550 unsigned char lcap_field ;
4551 mp_knot * path_p_field;
4552 mp_knot * pen_p_field;
4553 int miterlim_field ;
4554 mp_dash_object *dash_p_field;
4555 } mp_stroked_object;
4557 typedef struct mp_clip_object {
4559 mp_knot * path_p_field;
4562 typedef struct mp_bounds_object {
4564 mp_knot * path_p_field;
4567 typedef struct mp_special_object {
4569 char *pre_script_field;
4570 } mp_special_object ;
4572 typedef struct mp_edge_object {
4573 struct mp_graphic_object * body;
4574 struct mp_edge_object * _next;
4577 int _minx, _miny, _maxx, _maxy;
4578 int _width, _height, _depth, _ital_corr;
4582 @ @<Exported function headers@>=
4583 mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4586 mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4587 mp_graphic_object *p;
4590 case mp_fill_code: size = sizeof(mp_fill_object); break;
4591 case mp_stroked_code: size = sizeof(mp_stroked_object); break;
4592 case mp_text_code: size = sizeof(mp_text_object); break;
4593 case mp_start_clip_code: size = sizeof(mp_clip_object); break;
4594 case mp_start_bounds_code: size = sizeof(mp_bounds_object); break;
4595 case mp_special_code: size = sizeof(mp_special_object); break;
4596 default: size = sizeof(mp_graphic_object); break;
4598 p = (mp_graphic_object *)mp_xmalloc(mp,1,size);
4604 @ We need to keep track of several parameters from the \ps\ graphics state.
4606 This allows us to be sure that \ps\ has the correct values when they are
4607 needed without wasting time and space setting them unnecessarily.
4609 @d gs_red mp->ps->gs_state->red_field
4610 @d gs_green mp->ps->gs_state->green_field
4611 @d gs_blue mp->ps->gs_state->blue_field
4612 @d gs_black mp->ps->gs_state->black_field
4613 @d gs_colormodel mp->ps->gs_state->colormodel_field
4614 @d gs_ljoin mp->ps->gs_state->ljoin_field
4615 @d gs_lcap mp->ps->gs_state->lcap_field
4616 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4617 @d gs_miterlim mp->ps->gs_state->miterlim_field
4618 @d gs_dash_p mp->ps->gs_state->dash_p_field
4619 @d gs_dash_init_done mp->ps->gs_state->dash_done_field
4620 @d gs_previous mp->ps->gs_state->previous_field
4621 @d gs_width mp->ps->gs_state->width_field
4624 typedef struct _gs_state {
4626 scaled green_field ;
4628 scaled black_field ;
4629 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4630 quarterword colormodel_field ;
4631 /* the current colormodel */
4632 quarterword ljoin_field ;
4633 quarterword lcap_field ;
4634 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4635 quarterword adj_wx_field ;
4636 /* what resolution-dependent adjustment applies to the width */
4637 scaled miterlim_field ;
4638 /* the value from the last \&{setmiterlimit} command */
4639 mp_dash_object * dash_p_field ;
4640 /* edge structure for last \&{setdash} command */
4641 boolean dash_done_field ; /* to test for initial \&{setdash} */
4642 struct _gs_state * previous_field ;
4643 /* backlink to the previous |_gs_state| structure */
4644 scaled width_field ;
4645 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4650 struct _gs_state * gs_state;
4653 mp->ps->gs_state=NULL;
4655 @ @<Dealloc variables@>=
4656 mp_xfree(mp->ps->gs_state);
4658 @ To avoid making undue assumptions about the initial graphics state, these
4659 parameters are given special values that are guaranteed not to match anything
4660 in the edge structure being shipped out. On the other hand, the initial color
4661 should be black so that the translation of an all-black picture will have no
4662 \&{setcolor} commands. (These would be undesirable in a font application.)
4663 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4664 to recover from a situation where we have lost track of the graphics state.
4667 void mp_gs_unknown_graphics_state (MP mp,scaled c) ;
4670 @d mp_void (null+1) /* a null pointer different from |null| */
4672 @c void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4673 struct _gs_state *p; /* to shift graphic states around */
4674 if ( (c==0)||(c==-1) ) {
4675 if ( mp->ps->gs_state==NULL ) {
4676 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4679 while ( gs_previous!=NULL ) {
4681 mp_xfree(mp->ps->gs_state);
4685 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4686 gs_colormodel=mp_uninitialized_model;
4691 gs_dash_init_done=false;
4693 } else if ( c==1 ) {
4694 p= mp->ps->gs_state;
4695 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4696 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4698 } else if ( c==2 ) {
4700 mp_xfree(mp->ps->gs_state);
4706 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4707 that \ps's idea of the graphics state agrees with what is stored in the object.
4710 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ;
4713 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) {
4714 /* get ready to output graphical object |p| */
4715 mp_knot *pp, *path_p; /* for list manipulation */
4717 scaled wx,wy,ww; /* dimensions of pen bounding box */
4718 boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4719 integer tx,ty; /* temporaries for computing |adj_wx| */
4720 if ( gr_has_color(p) )
4721 @<Make sure \ps\ will use the right color for object~|p|@>;
4722 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) {
4723 if (gr_type(p)==mp_fill_code) {
4724 pp = gr_pen_p((mp_fill_object *)p);
4725 path_p = gr_path_p((mp_fill_object *)p);
4727 pp = gr_pen_p((mp_stroked_object *)p);
4728 path_p = gr_path_p((mp_stroked_object *)p);
4731 if ( pen_is_elliptical(pp) ) {
4732 @<Generate \ps\ code that sets the stroke width to the
4733 appropriate rounded value@>;
4734 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4735 @<Decide whether the line cap parameter matters and set it if necessary@>;
4736 @<Set the other numeric parameters as needed for object~|p|@>;
4739 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
4742 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4743 if ( gr_type(p)==mp_stroked_code ) {
4744 mp_stroked_object *ts = (mp_stroked_object *)p;
4745 if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) )
4746 if ( gs_lcap!=gr_lcap_val(ts) ) {
4748 mp_ps_print_char(mp, ' ');
4749 mp_ps_print_char(mp, '0'+gr_lcap_val(ts));
4750 mp_ps_print_cmd(mp, " setlinecap"," lc");
4751 gs_lcap=gr_lcap_val(ts);
4756 @d set_ljoin_miterlim(p)
4757 if ( gs_ljoin!=gr_ljoin_val(p) ) {
4759 mp_ps_print_char(mp, ' ');
4760 mp_ps_print_char(mp, '0'+gr_ljoin_val(p));
4761 mp_ps_print_cmd(mp, " setlinejoin"," lj");
4762 gs_ljoin=gr_ljoin_val(p);
4764 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4766 mp_ps_print_char(mp, ' ');
4767 mp_ps_print_scaled(mp, gr_miterlim_val(p));
4768 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4769 gs_miterlim=gr_miterlim_val(p);
4772 @<Set the other numeric parameters as needed for object~|p|@>=
4773 if ( gr_type(p)==mp_stroked_code ) {
4774 mp_stroked_object *ts = (mp_stroked_object *)p;
4775 set_ljoin_miterlim(ts);
4777 mp_fill_object *ts = (mp_fill_object *)p;
4778 set_ljoin_miterlim(ts);
4782 @d set_color_objects(pq)
4783 object_color_model = pq->color_model_field;
4784 object_color_a = pq->color_field._a_val;
4785 object_color_b = pq->color_field._b_val;
4786 object_color_c = pq->color_field._c_val;
4787 object_color_d = pq->color_field._d_val;
4789 @<Make sure \ps\ will use the right color for object~|p|@>=
4791 int object_color_model;
4792 int object_color_a, object_color_b, object_color_c, object_color_d ;
4793 if (gr_type(p) == mp_fill_code) {
4794 mp_fill_object *pq = (mp_fill_object *)p;
4795 set_color_objects(pq);
4796 } else if (gr_type(p) == mp_stroked_code) {
4797 mp_stroked_object *pq = (mp_stroked_object *)p;
4798 set_color_objects(pq);
4800 mp_text_object *pq = (mp_text_object *)p;
4801 set_color_objects(pq);
4804 if ( object_color_model==mp_rgb_model) {
4805 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)||
4806 (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) {
4807 gs_red = object_color_a;
4808 gs_green = object_color_b;
4809 gs_blue = object_color_c;
4811 gs_colormodel=mp_rgb_model;
4813 mp_ps_print_char(mp, ' ');
4814 mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
4815 mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
4816 mp_ps_print_scaled(mp, gs_blue);
4817 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4820 } else if ( object_color_model==mp_cmyk_model) {
4821 if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)||
4822 (gs_blue!=object_color_c)||(gs_black!=object_color_d)||
4823 (gs_colormodel!=mp_cmyk_model) ) {
4824 gs_red = object_color_a;
4825 gs_green = object_color_b;
4826 gs_blue = object_color_c;
4827 gs_black = object_color_d;
4828 gs_colormodel=mp_cmyk_model;
4830 mp_ps_print_char(mp, ' ');
4831 mp_ps_print_scaled(mp, gs_red);
4832 mp_ps_print_char(mp, ' ');
4833 mp_ps_print_scaled(mp, gs_green);
4834 mp_ps_print_char(mp, ' ');
4835 mp_ps_print_scaled(mp, gs_blue);
4836 mp_ps_print_char(mp, ' ');
4837 mp_ps_print_scaled(mp, gs_black);
4838 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4841 } else if ( object_color_model==mp_grey_model ) {
4842 if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) {
4843 gs_red = object_color_a;
4847 gs_colormodel=mp_grey_model;
4849 mp_ps_print_char(mp, ' ');
4850 mp_ps_print_scaled(mp, gs_red);
4851 mp_ps_print_cmd(mp, " setgray"," G");
4854 } else if ( object_color_model==mp_no_model ) {
4855 gs_colormodel=mp_no_model;
4859 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4860 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4861 @:setwidth}{\&{setwidth}command@>
4862 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4863 generate \ps\ code that computes the rounded value. For non-circular pens, the
4864 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4865 have the computed width.
4867 Rounding the width to whole pixels is not likely to improve the appearance of
4868 diagonal or curved strokes, but we do it anyway for consistency. The
4869 \&{truncate} command generated here tends to make all the strokes a little
4870 @:truncate}{\&{truncate} command@>
4871 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4872 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4873 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4876 @<Generate \ps\ code that sets the stroke width...@>=
4877 @<Set |wx| and |wy| to the width and height of the bounding box for
4879 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4880 important and set |adj_wx| and |ww| accordingly@>;
4881 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4884 mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
4886 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4888 if ( mp->internal[mp_procset]>0 ) {
4890 mp_ps_print_char(mp, ' ');
4891 mp_ps_print_scaled(mp, ww);
4892 mp_ps_print(mp, " vlw");
4895 mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
4896 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4903 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4904 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4905 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4906 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4908 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4909 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4912 @ The path is considered ``essentially horizontal'' if its range of
4913 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4914 vertical'' paths are detected similarly. This code ensures that no component
4915 of the pen transformation is more that |aspect_bound*(ww+1)|.
4917 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4918 more than this factor */
4923 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4925 if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound;
4926 else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound;
4927 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
4928 else { ww=wx; adj_wx=true; }
4930 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4931 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4932 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4933 bounding-box computation.
4936 boolean mp_gr_coord_rangeOK (mp_knot *h,
4937 small_number zoff, scaled dz);
4940 boolean mp_gr_coord_rangeOK (mp_knot *h,
4941 small_number zoff, scaled dz) {
4942 mp_knot *p; /* for scanning the path form |h| */
4943 scaled zlo,zhi; /* coordinate range so far */
4944 scaled z; /* coordinate currently being tested */
4945 if (zoff==do_x_loc) {
4949 while ( gr_right_type(p)!=mp_endpoint ) {
4951 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4952 p=gr_next_knot(p); z=gr_left_x(p);
4953 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4955 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4962 while ( gr_right_type(p)!=mp_endpoint ) {
4964 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4965 p=gr_next_knot(p); z=gr_left_y(p);
4966 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4968 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4975 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4977 else if ( z>zhi ) zhi=z;
4978 if ( zhi-zlo>dz ) return false
4980 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4981 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4982 @:stroke}{\&{stroke} command@>
4983 @:fill}{\&{fill} command@>
4984 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4987 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4988 if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) {
4994 if ( gs_dash_p!=NULL || gs_dash_init_done == false) {
4995 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4997 gs_dash_init_done=true;
4999 } else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) {
5000 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
5004 scaled mp_gr_get_pen_scale (MP mp, mp_knot *p) ;
5008 scaled mp_gr_get_pen_scale (MP mp, mp_knot *p) {
5009 return mp_sqrt_det(mp,
5010 gr_left_x(p)-gr_x_coord(p), gr_right_x(p)-gr_x_coord(p),
5011 gr_left_y(p)-gr_y_coord(p), gr_right_y(p)-gr_y_coord(p));
5015 @ The original code had a check here to ensure that the result from
5016 |mp_take_scaled| did not go out of bounds.
5018 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5020 if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array_field==NULL)) {
5021 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5025 mp_ps_print(mp, " [");
5026 for (i=0; *(hh->array_field+i) != -1;i++) {
5028 mp_ps_print_scaled(mp, *(hh->array_field+i));
5029 mp_ps_print_char(mp, ' ') ;
5032 mp_ps_print(mp, "] ");
5033 mp_ps_print_scaled(mp, hh->offset_field);
5034 mp_ps_print_cmd(mp, " setdash"," sd");
5039 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ;
5041 @ This function test if |h| and |hh| represent the same dash pattern.
5044 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) {
5047 if ( h==hh ) ret=true;
5048 else if ( (h==NULL)||(hh==NULL) ) ret=false;
5049 else if ( h->offset_field!=hh->offset_field ) ret=false;
5050 else if ( h->array_field == hh->array_field) ret=true;
5051 else if ( h->array_field == NULL || hh->array_field == NULL) ret=false;
5052 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5056 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5058 while (*(h->array_field+i)!=-1 &&
5059 *(hh->array_field+i)!=-1 &&
5060 *(h->array_field+i) == *(hh->array_field+i)) i++;
5062 if (*(h->array_field+(i))==-1 && *(hh->array_field+(i)) == -1)
5067 @ When stroking a path with an elliptical pen, it is necessary to transform
5068 the coordinate system so that a unit circular pen will have the desired shape.
5069 To keep this transformation local, we enclose it in a
5070 $$\&{gsave}\ldots\&{grestore}$$
5071 block. Any translation component must be applied to the path being stroked
5072 while the rest of the transformation must apply only to the pen.
5073 If |fill_also=true|, the path is to be filled as well as stroked so we must
5074 insert commands to do this after giving the path.
5077 void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) ;
5080 @c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) {
5081 /* generate an elliptical pen stroke from object |h| */
5082 scaled txx,txy,tyx,tyy; /* transformation parameters */
5083 mp_knot *p; /* the pen to stroke with */
5084 scaled d1,det; /* for tweaking transformation parameters */
5085 integer s; /* also for tweaking transformation paramters */
5086 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5088 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5090 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5091 if (gr_type(h)==mp_fill_code) {
5092 mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h));
5094 mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h));
5096 if ( mp->internal[mp_procset]==0 ) {
5097 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5098 @<Issue \ps\ commands to transform the coordinate system@>;
5099 mp_ps_print(mp, " stroke");
5100 if ( transformed ) mp_ps_print(mp, " grestore");
5102 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5103 if ( (txy!=0)||(tyx!=0) ) {
5104 mp_ps_print(mp, " [");
5105 mp_ps_pair_out(mp, txx,tyx);
5106 mp_ps_pair_out(mp, txy,tyy);
5107 mp_ps_print(mp, "0 0] t");
5108 } else if ((txx!=unity)||(tyy!=unity) ) {
5109 mp_ps_pair_out(mp,txx,tyy);
5110 mp_ps_print(mp, " s");
5112 mp_ps_print(mp, " S");
5113 if ( transformed ) mp_ps_print(mp, " Q");
5118 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5119 if (gr_type(h)==mp_fill_code) {
5120 p=gr_pen_p((mp_fill_object *)h);
5122 p=gr_pen_p((mp_stroked_object *)h);
5128 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5129 mp_ps_print_nl(mp, "");
5130 mp_ps_print_cmd(mp, "gsave ","q ");
5131 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5132 mp_ps_print(mp, "translate ");
5139 mp_ps_print_nl(mp, "");
5141 @<Adjust the transformation to account for |gs_width| and output the
5142 initial \&{gsave} if |transformed| should be |true|@>
5144 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5145 if ( gs_width!=unity ) {
5146 if ( gs_width==0 ) {
5147 txx=unity; tyy=unity;
5149 txx=mp_make_scaled(mp, txx,gs_width);
5150 txy=mp_make_scaled(mp, txy,gs_width);
5151 tyx=mp_make_scaled(mp, tyx,gs_width);
5152 tyy=mp_make_scaled(mp, tyy,gs_width);
5155 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5156 if ( (! transformed) ){
5157 mp_ps_print_cmd(mp, "gsave ","q ");
5162 @ @<Issue \ps\ commands to transform the coordinate system@>=
5163 if ( (txy!=0)||(tyx!=0) ){
5165 mp_ps_print_char(mp, '[');
5166 mp_ps_pair_out(mp, txx,tyx);
5167 mp_ps_pair_out(mp, txy,tyy);
5168 mp_ps_print(mp, "0 0] concat");
5169 } else if ( (txx!=unity)||(tyy!=unity) ){
5171 mp_ps_pair_out(mp, txx,tyy);
5172 mp_ps_print(mp, "scale");
5175 @ The \ps\ interpreter will probably abort if it encounters a singular
5176 transformation matrix. The determinant must be large enough to ensure that
5177 the printed representation will be nonsingular. Since the printed
5178 representation is always within $2^{-17}$ of the internal |scaled| value, the
5179 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5180 the magnitudes of |txx/65536|, |txy/65536|, etc.
5182 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5183 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5185 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5186 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5187 d1=4*aspect_bound+1;
5188 if ( abs(det)<d1 ) {
5189 if ( det>=0 ) { d1=d1-det; s=1; }
5190 else { d1=-d1-det; s=-1; };
5192 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5193 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5194 else txx=txx+(d1+s*abs(tyy)) / tyy;
5196 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5197 else txy=txy+(d1+s*abs(tyx)) / tyx;
5201 @ Here is a simple routine that just fills a cycle.
5204 void mp_gr_ps_fill_out (MP mp, mp_knot *p);
5207 void mp_gr_ps_fill_out (MP mp, mp_knot *p) { /* fill cyclic path~|p| */
5208 mp_gr_ps_path_out(mp, p);
5209 mp_ps_print_cmd(mp, " fill"," F");
5213 @ A text node may specify an arbitrary transformation but the usual case
5214 involves only shifting, scaling, and occasionally rotation. The purpose
5215 of |choose_scale| is to select a scale factor so that the remaining
5216 transformation is as ``nice'' as possible. The definition of ``nice''
5217 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5218 nice because they work out well for bitmap fonts. The code here selects
5219 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5220 non-shifting part of the transformation matrix. It is careful to avoid
5221 additions that might cause undetected overflow.
5224 scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) ;
5226 @ @c scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) {
5227 /* |p| should point to a text node */
5228 scaled a,b,c,d,ad,bc; /* temporary values */
5233 if ( a<0 ) negate(a);
5234 if ( b<0 ) negate(b);
5235 if ( c<0 ) negate(c);
5236 if ( d<0 ) negate(d);
5239 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5242 @ The potential overflow here is caused by the fact the returned value
5243 has to fit in a |name_type|, which is a quarterword.
5245 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
5248 quarterword mp_size_index (MP mp, font_number f, scaled s) ;
5251 quarterword mp_size_index (MP mp, font_number f, scaled s) {
5252 pointer p,q; /* the previous and current font size nodes */
5253 quarterword i; /* the size index for |q| */
5254 q=mp->font_sizes[f];
5257 if ( abs(s-sc_factor(q))<=fscale_tolerance )
5260 { p=q; q=link(q); incr(i); };
5261 if ( i==max_quarterword )
5262 mp_overflow(mp, "sizes per font",max_quarterword);
5263 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
5265 q=mp_get_node(mp, font_size_size);
5267 if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
5272 scaled mp_indexed_size (MP mp,font_number f, quarterword j);
5275 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
5276 pointer p; /* a font size node */
5277 quarterword i; /* the size index for |p| */
5278 p=mp->font_sizes[f];
5280 if ( p==null ) mp_confusion(mp, "size");
5283 if ( p==null ) mp_confusion(mp, "size");
5285 return sc_factor(p);
5289 void mp_clear_sizes (MP mp) ;
5291 @ @c void mp_clear_sizes (MP mp) {
5292 font_number f; /* the font whose size list is being cleared */
5293 pointer p; /* current font size nodes */
5294 for (f=null_font+1;f<=mp->last_fnum;f++) {
5295 while ( mp->font_sizes[f]!=null ) {
5296 p=mp->font_sizes[f];
5297 mp->font_sizes[f]=link(p);
5298 mp_free_node(mp, p,font_size_size);
5303 @ A text node may specify an arbitrary transformation but the usual case
5304 involves only shifting, scaling, and occasionally rotation. The purpose
5305 of |choose_scale| is to select a scale factor so that the remaining
5306 transformation is as ``nice'' as possible. The definition of ``nice''
5307 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5308 nice because they work out well for bitmap fonts. The code here selects
5309 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5310 non-shifting part of the transformation matrix. It is careful to avoid
5311 additions that might cause undetected overflow.
5313 @<Declare the \ps\ output procedures@>=
5314 scaled mp_choose_scale (MP mp, mp_graphic_object *p) ;
5316 @ @c scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
5317 /* |p| should point to a text node */
5318 scaled a,b,c,d,ad,bc; /* temporary values */
5323 if ( (a<0) ) negate(a);
5324 if ( (b<0) ) negate(b);
5325 if ( (c<0) ) negate(c);
5326 if ( (d<0) ) negate(d);
5329 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5332 @ There may be many sizes of one font and we need to keep track of the
5333 characters used for each size. This is done by keeping a linked list of
5334 sizes for each font with a counter in each text node giving the appropriate
5335 position in the size list for its font.
5337 @d font_size_size 2 /* size of a font size node */
5341 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size);
5344 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) {
5345 mp_graphic_object * p;
5347 while ( p!= NULL ) {
5348 if ( gr_type(p)==mp_text_code ) {
5349 if ( gr_font_n(p)!=null_font ) {
5350 if ( gr_name_type(p)==next_size )
5351 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p));
5358 @ @<Unmark all marked characters@>=
5359 for (f=null_font+1;f<=mp->last_fnum;f++) {
5360 if ( mp->font_sizes[f]!=null ) {
5361 mp_unmark_font(mp, f);
5362 mp->font_sizes[f]=null;
5366 @ @<Scan all the text nodes and mark the used ...@>=
5369 if ( gr_type(p)==mp_text_code ) {
5371 if (f!=null_font ) {
5372 switch (prologues) {
5375 mp->font_sizes[f] = mp_void;
5376 mp_mark_string_chars(mp, f, gr_text_p(p));
5377 if (mp_has_fm_entry(mp,f,NULL) ) {
5378 if (mp->font_enc_name[f]==NULL )
5379 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
5380 mp->font_ps_name[f] = mp_fm_font_name(mp,f);
5384 mp->font_sizes[f]=mp_void;
5387 gr_name_type(p)=mp_size_index(mp, f,mp_choose_scale(mp, p));
5388 if ( gr_name_type(p)==0 )
5389 mp_mark_string_chars(mp, f, gr_text_p(p));
5398 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5400 @<Exported function ...@>=
5401 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) ;
5404 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) {
5405 mp_graphic_object *p;
5406 scaled ds,scf; /* design size and scale factor for a text node */
5407 font_number f; /* for loops over fonts while (un)marking characters */
5408 boolean transformed; /* is the coordinate system being transformed? */
5409 MP mp = hh->_parent;
5412 mp->jump_buf = &buf;
5413 if (setjmp(*(mp->jump_buf)))
5416 if (mp->history >= mp_fatal_error_stop ) return 1;
5418 prologues = (mp->internal[mp_prologues]>>16);
5420 procset = (mp->internal[mp_procset]>>16);
5421 mp_open_output_file(mp);
5422 mp_print_initial_comment(mp, hh, prologues);
5424 @<Unmark all marked characters@>;
5425 if ( prologues==2 || prologues==3 ) {
5426 mp_reload_encodings(mp);
5428 @<Scan all the text nodes and mark the used characters@>;
5429 if ( prologues==2 || prologues==3 ) {
5430 mp_print_improved_prologue(mp, hh, prologues, procset);
5432 mp_print_prologue(mp, hh, prologues, procset);
5434 mp_gs_unknown_graphics_state(mp, 0);
5437 if ( gr_has_color(p) ) {
5438 @<Write |pre_script| of |p|@>;
5440 mp_gr_fix_graphics_state(mp, p);
5441 switch (gr_type(p)) {
5443 if ( gr_pen_p((mp_fill_object *)p)==NULL ) {
5444 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5445 } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) ) {
5446 mp_gr_stroke_ellipse(mp, p,true);
5448 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5449 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5451 if ( gr_post_script((mp_fill_object *)p)!=NULL ) {
5452 mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p));
5456 case mp_stroked_code:
5457 if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) )
5458 mp_gr_stroke_ellipse(mp, p,false);
5460 mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p));
5462 if ( gr_post_script((mp_stroked_object *)p)!=NULL ) {
5463 mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p));
5468 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5470 scf=mp_gr_choose_scale(mp, p);
5472 scf=mp_indexed_size(mp, gr_font_n(p), gr_name_type(p));
5473 @<Shift or transform as necessary before outputting text node~|p| at scale
5474 factor~|scf|; set |transformed:=true| if the original transformation must
5476 mp_ps_string_out(mp, gr_text_p(p));
5477 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5478 @<Print the size information and \ps\ commands for text node~|p|@>;
5481 if ( gr_post_script((mp_text_object *)p)!=NULL ) {
5482 mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp);
5485 case mp_start_clip_code:
5486 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5487 mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p));
5488 mp_ps_print_cmd(mp, " clip"," W");
5490 if ( mp->internal[mp_restore_clip_color]>0 )
5491 mp_gs_unknown_graphics_state(mp, 1);
5493 case mp_stop_clip_code:
5494 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5496 if ( mp->internal[mp_restore_clip_color]>0 )
5497 mp_gs_unknown_graphics_state(mp, 2);
5499 mp_gs_unknown_graphics_state(mp, -1);
5501 case mp_start_bounds_code:
5502 case mp_stop_bounds_code:
5504 case mp_special_code:
5506 mp_special_object *ps = (mp_special_object *)p;
5507 mp_ps_print_nl (mp, gr_pre_script(ps));
5508 mp_ps_print_ln (mp);
5511 } /* all cases are enumerated */
5514 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
5515 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
5516 (mp->close_file)(mp,mp->ps_file);
5523 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) ;
5526 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) {
5527 return mp_gr_ship_out (hh, prologues, procset, true);
5535 @d do_write_prescript(a,b) {
5536 if ( (gr_pre_script((b *)a))!=NULL ) {
5537 mp_ps_print_nl (mp, gr_pre_script((b *)a));
5542 @<Write |pre_script| of |p|@>=
5544 if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); }
5545 else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); }
5546 else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); }
5549 @ The envelope of a cyclic path~|q| could be computed by calling
5550 |make_envelope| once for |q| and once for its reversal. We don't do this
5551 because it would fail color regions that are covered by the pen regardless
5552 of where it is placed on~|q|.
5554 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5555 if ( gr_left_type(q)!=mp_endpoint ) {
5556 gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5557 gr_right_type(q)=mp_endpoint;
5562 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5564 mp_ps_print_char(mp, ' ');
5565 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5566 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5567 mp_ps_print(mp, " fshow");
5569 mp_ps_print_cmd(mp, " grestore"," Q")
5573 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5574 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5575 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5576 if ( transformed ) {
5577 mp_ps_print_cmd(mp, "gsave [", "q [");
5578 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5579 mp_make_scaled(mp, gr_tyx_val(p),scf));
5580 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5581 mp_make_scaled(mp, gr_tyy_val(p),scf));
5582 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5583 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5585 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5586 mp_ps_print_cmd(mp, "moveto","m");
5591 void mp_gr_toss_objects ( mp_edge_object *hh) ;
5592 void mp_gr_toss_object (mp_graphic_object *p) ;
5595 void mp_gr_toss_object (mp_graphic_object *p) {
5597 mp_stroked_object *ts;
5599 switch (gr_type(p)) {
5601 tf = (mp_fill_object *)p;
5602 mp_xfree(gr_pre_script(tf));
5603 mp_xfree(gr_post_script(tf));
5604 mp_gr_toss_knot_list(mp,gr_pen_p(tf));
5605 mp_gr_toss_knot_list(mp,gr_path_p(tf));
5606 mp_gr_toss_knot_list(mp,gr_htap_p(p));
5608 case mp_stroked_code:
5609 ts = (mp_stroked_object *)p;
5610 mp_xfree(gr_pre_script(ts));
5611 mp_xfree(gr_post_script(ts));
5612 mp_gr_toss_knot_list(mp,gr_pen_p(ts));
5613 mp_gr_toss_knot_list(mp,gr_path_p(ts));
5614 if (gr_dash_p(p)!=NULL)
5615 mp_gr_toss_dashes (mp,gr_dash_p(p));
5618 tt = (mp_text_object *)p;
5619 mp_xfree(gr_pre_script(tt));
5620 mp_xfree(gr_post_script(tt));
5621 mp_xfree(gr_text_p(p));
5622 mp_xfree(gr_font_name(p));
5624 case mp_start_clip_code:
5625 mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p));
5627 case mp_start_bounds_code:
5628 mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p));
5630 case mp_stop_clip_code:
5631 case mp_stop_bounds_code:
5633 case mp_special_code:
5634 mp_xfree(gr_pre_script((mp_special_object *)p));
5636 } /* all cases are enumerated */
5642 void mp_gr_toss_objects (mp_edge_object *hh) {
5643 mp_graphic_object *p, *q;
5647 mp_gr_toss_object(p);
5654 mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ;
5658 mp_gr_copy_object (MP mp, mp_graphic_object *p) {
5660 mp_stroked_object *ts;
5663 mp_bounds_object *tb;
5664 mp_special_object *tp;
5665 mp_graphic_object *q = NULL;
5666 switch (gr_type(p)) {
5668 tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code);
5669 gr_pre_script(tf) = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p));
5670 gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p));
5671 gr_path_p(tf) = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p));
5672 gr_htap_p(tf) = mp_gr_copy_path(mp,gr_htap_p(p));
5673 gr_pen_p(tf) = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p));
5674 q = (mp_graphic_object *)tf;
5676 case mp_stroked_code:
5677 ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code);
5678 gr_pre_script(ts) = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p));
5679 gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p));
5680 gr_path_p(ts) = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p));
5681 gr_pen_p(ts) = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p));
5682 gr_dash_p(ts) = mp_gr_copy_dashes(mp,gr_dash_p(p));
5683 q = (mp_graphic_object *)ts;
5686 tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code);
5687 gr_pre_script(tt) = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p));
5688 gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p));
5689 gr_text_p(tt) = mp_xstrdup(mp, gr_text_p(p));
5690 gr_font_name(tt) = mp_xstrdup(mp, gr_font_name(p));
5691 q = (mp_graphic_object *)tt;
5693 case mp_start_clip_code:
5694 tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code);
5695 gr_path_p(tc) = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p));
5696 q = (mp_graphic_object *)tc;
5698 case mp_start_bounds_code:
5699 tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code);
5700 gr_path_p(tb) = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p));
5701 q = (mp_graphic_object *)tb;
5703 case mp_special_code:
5704 tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code);
5705 gr_pre_script(tp) = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p));
5706 q = (mp_graphic_object *)tp;
5708 case mp_stop_clip_code:
5709 q = mp_new_graphic_object(mp, mp_stop_clip_code);
5711 case mp_stop_bounds_code:
5712 q = mp_new_graphic_object(mp, mp_stop_bounds_code);
5714 } /* all cases are enumerated */