1 % $Id: mp.web,v 1.8 2005/08/24 10:54:02 taco Exp $
2 % MetaPost, by John Hobby. Public domain.
4 % Much of this program was copied with permission from MF.web Version 1.9
5 % It interprets a language very similar to D.E. Knuth's METAFONT, but with
6 % changes designed to make it more suitable for PostScript output.
8 % TeX is a trademark of the American Mathematical Society.
9 % METAFONT is a trademark of Addison-Wesley Publishing Company.
10 % PostScript is a trademark of Adobe Systems Incorporated.
12 % Here is TeX material that gets inserted after \input webmac
13 \def\hang{\hangindent 3em\noindent\ignorespaces}
14 \def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
17 \def\ph{\hbox{Pascal-H}}
18 \def\psqrt#1{\sqrt{\mathstrut#1}}
20 \def\pct!{{\char`\%}} % percent sign in ordinary text
21 \font\tenlogo=logo10 % font used for the METAFONT logo
23 \def\MF{{\tenlogo META}\-{\tenlogo FONT}}
24 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
25 \def\<#1>{$\langle#1\rangle$}
26 \def\section{\mathhexbox278}
27 \let\swap=\leftrightarrow
28 \def\round{\mathop{\rm round}\nolimits}
29 \mathchardef\vb="026A % synonym for `\|'
30 \def\[#1]{} % from pascal web
31 \def\(#1){} % this is used to make section names sort themselves better
32 \def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
34 \let\?=\relax % we want to be able to \write a \?
36 \def\title{MetaPost \ps\ output}
37 \def\topofcontents{\hsize 5.5in
38 \vglue -30pt plus 1fil minus 1.5in
39 \def\?##1]{\hbox to 1in{\hfil##1.\ }}
41 \def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
50 @d unity 0200000 /* $2^{16}$, represents 1.00000 */
51 @d el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
52 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
53 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
54 @d negate(A) (A)=-(A) /* change the sign of a variable */
57 @d print_err(A) mp_print_err(mp,(A))
58 @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
68 #include "psout.h" /* external header */
69 #include "mpmp.h" /* internal header */
70 #include "mppsout.h" /* internal header */
73 @<Static variables in the outer block@>
75 @ There is a small bit of code from the backend that bleads through
76 to the frontend because I do not know how to set up the includes
77 properly. Those are the definitions of |struct libavl_allocator|
78 and |typedef struct psout_data_struct * psout_data|.
80 The |libavl_allocator| is a trick that makes sure that frontends
81 do not need |avl.h|, and the |psout_data| is needed for the backend
86 typedef struct psout_data_struct {
89 @<Exported function headers@>
91 @ @<Exported function headers@>=
92 void mp_backend_initialize (MP mp) ;
93 void mp_backend_free (MP mp) ;
96 @c void mp_backend_initialize (MP mp) {
97 mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
98 @<Set initial values@>;
100 void mp_backend_free (MP mp) {
101 @<Dealloc variables@>;
109 @ Writing to ps files
113 /* the number of characters on the current \ps\ file line */
115 @ @<Set initial values@>=
116 mp->ps->ps_offset = 0;
120 @d wps(A) (mp->write_ascii_file)(mp,mp->ps_file,(A))
124 (mp->write_ascii_file)(mp,mp->ps_file,(char *)ss);
126 @d wps_cr (mp->write_ascii_file)(mp,mp->ps_file,"\n")
127 @d wps_ln(A) { wterm_cr; (mp->write_ascii_file)(mp,mp->ps_file,(A)); }
130 void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
136 void mp_ps_print_char (MP mp, ASCII_code s) { /* prints a single character */
138 wps_cr; mp->ps->ps_offset=0;
140 wps_chr(s); incr(mp->ps->ps_offset);
145 void mp_ps_do_print (MP mp, const char *ss, unsigned int len) { /* prints string |s| */
148 mp_ps_print_char(mp, ss[j]); incr(j);
152 @ Deciding where to break the ps output line.
154 @d ps_room(A) if ( (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) {
155 mp_ps_print_ln(mp); /* optional line break */
159 void mp_ps_print (MP mp, const char *ss) {
161 mp_ps_do_print(mp, ss, strlen(ss));
164 @ The procedure |print_nl| is like |print|, but it makes sure that the
165 string appears at the beginning of a new line.
168 void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */
169 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
173 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
176 void mp_ps_print_the_digs (MP mp, eight_bits k) {
177 /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
179 mp_ps_print_char(mp, '0'+mp->dig[k]);
183 @ The following procedure, which prints out the decimal representation of a
184 given integer |n|, has been written carefully so that it works properly
185 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
186 to negative arguments, since such operations are not implemented consistently
187 by all \PASCAL\ compilers.
190 void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */
191 integer m; /* used to negate |n| in possibly dangerous cases */
192 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
194 mp_ps_print_char(mp, '-');
195 if ( n>-100000000 ) {
198 m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
202 mp->dig[0]=0; incr(n);
207 mp->dig[k]=n % 10; n=n / 10; incr(k);
209 mp_ps_print_the_digs(mp, k);
213 void mp_ps_print_int (MP mp,integer n);
215 @ \MP\ also makes use of a trivial procedure to print two digits. The
216 following subroutine is usually called with a parameter in the range |0<=n<=99|.
219 void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */
221 mp_ps_print_char(mp, '0'+(n / 10));
222 mp_ps_print_char(mp, '0'+(n % 10));
225 @ Conversely, here is a procedure analogous to |print_int|. If the output
226 of this procedure is subsequently read by \MP\ and converted by the
227 |round_decimals| routine above, it turns out that the original value will
228 be reproduced exactly. A decimal point is printed only if the value is
229 not an integer. If there is more than one way to print the result with
230 the optimum number of digits following the decimal point, the closest
231 possible value is given.
233 The invariant relation in the \&{repeat} loop is that a sequence of
234 decimal digits yet to be printed will yield the original number if and only if
235 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
236 We can stop if and only if $f=0$ satisfies this condition; the loop will
237 terminate before $s$ can possibly become zero.
240 void mp_ps_print_scaled (MP mp,scaled s) {
241 scaled delta; /* amount of allowable inaccuracy */
243 mp_ps_print_char(mp, '-');
244 negate(s); /* print the sign, if negative */
246 mp_ps_print_int(mp, s / unity); /* print the integer part */
250 mp_ps_print_char(mp, '.');
253 s=s+0100000-(delta / 2); /* round the final digit */
254 mp_ps_print_char(mp, '0'+(s / unity));
262 @* \[44a] Dealing with font encodings.
264 First, here are a few helpers for parsing files
266 @d check_buf(size, buf_size)
267 if ((unsigned)(size) > (unsigned)(buf_size)) {
269 mp_snprintf(S,128,"buffer overflow: (%d,%d) at file %s, line %d",
270 size,buf_size, __FILE__, __LINE__ );
271 mp_fatal_error(mp,S);
274 @d append_char_to_buf(c, p, buf, buf_size) do {
277 if (c == 13 || c == EOF)
279 if (c != ' ' || (p > buf && p[-1] != 32)) {
280 check_buf(p - buf + 1, (buf_size));
285 @d append_eol(p, buf, buf_size) do {
286 check_buf(p - buf + 2, (buf_size));
287 if (p - buf > 1 && p[-1] != 10)
289 if (p - buf > 2 && p[-2] == 32) {
296 @d remove_eol(p, buf) do {
302 @d skip(p, c) if (*p == c) p++
303 @d strend(s) strchr(s, 0)
304 @d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
309 boolean loaded; /* the encoding has been loaded? */
310 char *file_name; /* encoding file name */
311 char *enc_name; /* encoding true name */
312 integer objnum; /* object number */
314 integer tounicode; /* object number of associated ToUnicode entry */
324 #define ENC_BUF_SIZE 0x1000
325 char enc_line[ENC_BUF_SIZE];
329 @d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file)
330 @d enc_close() (mp->close_file)(mp,mp->ps->enc_file)
333 int enc_getchar(MP mp) {
335 unsigned char abyte=0;
336 void *byte_ptr = &abyte;
337 (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len);
342 static boolean mp_enc_open (MP mp, char *n) {
343 mp->ps->enc_file=(mp->open_file)(mp,n, "r", mp_filetype_encoding);
344 if (mp->ps->enc_file!=NULL)
349 static void mp_enc_getline (MP mp) {
354 print_err("unexpected end of file");
357 p = mp->ps->enc_line;
359 c = enc_getchar (mp);
360 append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
362 append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
363 if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
366 static void mp_load_enc (MP mp, char *enc_name,
367 char **enc_encname, char **glyph_names){
368 char buf[ENC_BUF_SIZE], *p, *r;
371 int save_selector = mp->selector;
372 if (!mp_enc_open (mp,enc_name)) {
373 mp_print (mp,"cannot open encoding file for reading");
376 mp_normalize_selector(mp);
378 mp_print (mp, enc_name);
380 if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
381 remove_eol (r, mp->ps->enc_line);
382 print_err ("invalid encoding vector (a name or `[' missing): `");
383 mp_print(mp,mp->ps->enc_line);
387 while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
388 myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
389 memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
390 *(myname+(r-mp->ps->enc_line-1))=0;
391 *enc_encname = myname;
399 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
402 if (names_count > 256) {
403 print_err ("encoding vector contains more than 256 names");
406 if (mp_xstrcmp (buf, notdef) != 0)
407 glyph_names[names_count] = mp_xstrdup (mp,buf);
410 if (*r != 10 && *r != '%') {
411 if (str_prefix (r, "] def"))
414 remove_eol (r, mp->ps->enc_line);
416 ("invalid encoding vector: a name or `] def' expected: `");
417 mp_print(mp,mp->ps->enc_line);
423 r = mp->ps->enc_line;
428 mp->selector = save_selector;
430 static void mp_read_enc (MP mp, enc_entry * e) {
434 mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
438 @ |write_enc| is used to write either external encoding (given in map file) or
439 internal encoding (read from the font file);
440 the 2nd argument is a pointer to the encoding entry;
443 static void mp_write_enc (MP mp, enc_entry * e) {
448 if (e->objnum != 0) /* the encoding has been written already */
453 mp_ps_print(mp,"\n%%%%BeginResource: encoding ");
454 mp_ps_print(mp, e->enc_name);
455 mp_ps_print_nl(mp, "/");
456 mp_ps_print(mp, e->enc_name);
457 mp_ps_print(mp, " [ ");
458 foffset = strlen(e->file_name)+3;
459 for (i = 0; i < 256; i++) {
461 if (s+1+foffset>=80) {
466 mp_ps_print_char(mp,'/');
467 mp_ps_print(mp, g[i]);
468 mp_ps_print_char(mp,' ');
472 mp_ps_print_nl (mp,"] def\n");
473 mp_ps_print(mp,"%%%%EndResource");
477 @ All encoding entries go into AVL tree for fast search by name.
480 struct avl_table *enc_tree;
482 @ Memory management functions for avl
484 @<Static variables in the outer block@>=
485 static const char notdef[] = ".notdef";
488 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
489 static void avl_xfree (struct libavl_allocator *allocator, void *block);
492 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
494 return malloc (size);
496 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
502 struct libavl_allocator avl_xallocator;
504 @ @<Set initial...@>=
505 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
506 mp->ps->avl_xallocator.libavl_free= avl_xfree;
507 mp->ps->enc_tree = NULL;
510 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
512 return strcmp (((const enc_entry *) pa)->file_name,
513 ((const enc_entry *) pb)->file_name);
515 static enc_entry * mp_add_enc (MP mp, char *s) {
519 if (mp->ps->enc_tree == NULL) {
520 mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
523 p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
524 if (p != NULL) /* encoding already registered */
526 p = mp_xmalloc (mp,1,sizeof (enc_entry));
528 p->file_name = mp_xstrdup (mp,s);
531 p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
532 for (i = 0; i < 256; i++)
533 p->glyph_names[i] = (char *) notdef;
534 aa = avl_probe (mp->ps->enc_tree, p);
541 static void mp_destroy_enc_entry (void *pa, void *pb) {
544 p = (enc_entry *) pa;
546 mp_xfree (p->file_name);
547 if (p->glyph_names != NULL)
548 for (i = 0; i < 256; i++)
549 if (p->glyph_names[i] != notdef)
550 mp_xfree (p->glyph_names[i]);
551 mp_xfree (p->glyph_names);
556 static void enc_free (MP mp);
558 @ @c static void enc_free (MP mp) {
559 if (mp->ps->enc_tree != NULL)
560 avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
563 @ @<Exported function headers@>=
564 void mp_reload_encodings (MP mp) ;
567 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) ;
569 @ @c void mp_reload_encodings (MP mp) {
573 int lastfnum = mp->last_fnum;
574 for (f=null_font+1;f<=lastfnum;f++) {
575 if (mp->font_enc_name[f]!=NULL ) {
576 mp_xfree(mp->font_enc_name[f]);
577 mp->font_enc_name[f]=NULL;
579 if (mp_has_fm_entry (mp,f,&fm_cur)) {
580 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
581 e = fm_cur->encoding;
587 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) {
591 for (f=null_font+1;f<=lastfnum;f++) {
592 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
593 if (fm != NULL && (fm->ps_name != NULL)) {
594 if (is_reencoded (fm)) {
595 if (encodings_only || (!is_subsetted (fm))) {
597 mp_write_enc (mp, e);
598 /* clear for next run */
607 @* \[44b] Parsing font map files.
615 @d fm_close() (mp->close_file)(mp,mp->ps->fm_file)
616 @d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file)
619 int fm_getchar(MP mp) {
621 unsigned char abyte=0;
622 void *byte_ptr = &abyte;
623 (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
631 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
632 enum _ltype { MAPFILE, MAPLINE };
633 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
634 typedef struct mitem {
635 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
636 int type; /* map file or map line */
637 char *map_line; /* pointer to map file name or map line */
638 int lineno; /* line number in map file */
644 fm_entry *loaded_tfm_found;
645 fm_entry *avail_tfm_found;
646 fm_entry *non_tfm_found;
647 fm_entry *not_avail_tfm_found;
649 @ @<Set initial...@>=
650 mp->ps->mitem = NULL;
653 static const char nontfm[] = "<nontfm>";
656 @d read_field(r, q, buf) do {
658 while (*r != ' ' && *r != '\0')
666 fm->F = mp_xstrdup(mp,buf);
678 static fm_entry *new_fm_entry (MP mp) {
680 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
685 fm->subset_tag = NULL;
687 fm->tfm_num = null_font;
688 fm->tfm_avail = TFM_UNCHECKED;
696 fm->all_glyphs = false;
703 static void delete_fm_entry (fm_entry * fm) {
704 mp_xfree (fm->tfm_name);
705 mp_xfree (fm->ps_name);
706 mp_xfree (fm->ff_name);
707 mp_xfree (fm->subset_tag);
708 mp_xfree (fm->charset);
712 static ff_entry *new_ff_entry (MP mp) {
714 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
720 static void delete_ff_entry (ff_entry * ff) {
721 mp_xfree (ff->ff_name);
722 mp_xfree (ff->ff_path);
726 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
727 static char buf[SMALL_BUF_SIZE];
728 char *p = tfmname, *r = strend (p) - 1, *q = r;
729 while (q > p && isdigit (*q))
731 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
733 check_buf (q - p + 1, SMALL_BUF_SIZE);
734 strncpy (buf, p, (size_t) (q - p));
740 @ @<Exported function headers@>=
741 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
744 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
745 fm_entry *res = NULL;
746 res = mp_fm_lookup (mp, f);
750 return (res != NULL);
754 struct avl_table *tfm_tree;
755 struct avl_table *ps_tree;
756 struct avl_table *ff_tree;
758 @ @<Set initial...@>=
759 mp->ps->tfm_tree = NULL;
760 mp->ps->ps_tree = NULL;
761 mp->ps->ff_tree = NULL;
763 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
766 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
768 return strcmp (((const fm_entry *) pa)->tfm_name,
769 ((const fm_entry *) pb)->tfm_name);
772 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
774 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
776 const fm_entry *p1 = (const fm_entry *) pa;
777 const fm_entry *p2 = (const fm_entry *) pb;
779 assert (p1->ps_name != NULL && p2->ps_name != NULL);
780 if ((i = strcmp (p1->ps_name, p2->ps_name)))
782 cmp_return (p1->slant, p2->slant);
783 cmp_return (p1->extend, p2->extend);
784 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
785 (i = strcmp (p1->tfm_name, p2->tfm_name)))
790 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
792 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
794 return strcmp (((const ff_entry *) pa)->ff_name,
795 ((const ff_entry *) pb)->ff_name);
798 @ @c static void create_avl_trees (MP mp) {
799 if (mp->ps->tfm_tree == NULL) {
800 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
801 assert (mp->ps->tfm_tree != NULL);
803 if (mp->ps->ps_tree == NULL) {
804 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
805 assert (mp->ps->ps_tree != NULL);
807 if (mp->ps->ff_tree == NULL) {
808 mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
809 assert (mp->ps->ff_tree != NULL);
813 @ The function |avl_do_entry| is not completely symmetrical with regards
814 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
815 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
816 with the original version.
820 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
821 @d set_pslink(fm) ((fm)->links |= LINK_PS)
822 @d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
823 @d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
824 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
825 @d has_pslink(fm) ((fm)->links & LINK_PS)
828 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
834 /* handle |tfm_name| link */
836 if (strcmp (fp->tfm_name, nontfm)) {
837 p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
839 if (mode == FM_DUPIGNORE) {
840 mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
844 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
845 if (mp_has_font_size(mp,p->tfm_num)) {
847 "fontmap entry for `%s' has been used, replace/delete not allowed",
852 a = avl_delete (mp->ps->tfm_tree, p);
859 if (mode != FM_DELETE) {
860 aa = avl_probe (mp->ps->tfm_tree, fp);
866 /* handle |ps_name| link */
868 if (fp->ps_name != NULL) {
869 assert (fp->tfm_name != NULL);
870 p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
872 if (mode == FM_DUPIGNORE) {
874 "ps_name entry for `%s' already exists, duplicates ignored",
878 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
879 if (mp_has_font_size(mp,p->tfm_num)) {
880 /* REPLACE/DELETE not allowed */
882 "fontmap entry for `%s' has been used, replace/delete not allowed",
887 a = avl_delete (mp->ps->ps_tree, p);
890 if (!has_tfmlink (p))
894 if (mode != FM_DELETE) {
895 aa = avl_probe (mp->ps->ps_tree, fp);
901 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
902 return 1; /* deallocation of |fm_entry| structure required */
907 @ consistency check for map entry, with warn flag
910 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
914 if (fm->ps_name != NULL) {
915 if (is_basefont (fm)) {
916 if (is_fontfile (fm) && !is_included (fm)) {
918 mp_snprintf(s,128, "invalid entry for `%s': "
919 "font file must be included or omitted for base fonts",
925 } else { /* not a base font */
926 /* if no font file given, drop this entry */
927 /* |if (!is_fontfile (fm)) {
930 "invalid entry for `%s': font file missing",
939 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
942 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
948 if ((fm->slant != 0 || fm->extend != 0) &&
949 (is_truetype (fm))) {
952 "invalid entry for `%s': "
953 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
959 if (abs (fm->slant) > 1000) {
962 "invalid entry for `%s': too big value of SlantFont (%g)",
963 fm->tfm_name, fm->slant / 1000.0);
968 if (abs (fm->extend) > 2000) {
971 "invalid entry for `%s': too big value of ExtendFont (%g)",
972 fm->tfm_name, fm->extend / 1000.0);
978 !(is_truetype (fm) && is_included (fm) &&
979 is_subsetted (fm) && !is_reencoded (fm))) {
982 "invalid entry for `%s': "
983 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
992 @ returns true if s is one of the 14 std. font names; speed-trimmed.
994 @c static boolean check_basefont (char *s) {
995 static const char *basefont_names[] = {
997 "Courier-Bold", /* 1:12 */
998 "Courier-Oblique", /* 2:15 */
999 "Courier-BoldOblique", /* 3:19 */
1000 "Helvetica", /* 4:9 */
1001 "Helvetica-Bold", /* 5:14 */
1002 "Helvetica-Oblique", /* 6:17 */
1003 "Helvetica-BoldOblique", /* 7:21 */
1005 "Times-Roman", /* 9:11 */
1006 "Times-Bold", /* 10:10 */
1007 "Times-Italic", /* 11:12 */
1008 "Times-BoldItalic", /* 12:16 */
1009 "ZapfDingbats" /* 13:12 */
1011 static const int Index[] =
1012 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1015 const size_t n = strlen (s);
1019 if (n == 12) { /* three names have length 12 */
1022 k = 1; /* Courier-Bold */
1025 k = 11; /* Times-Italic */
1028 k = 13; /* ZapfDingbats */
1035 if (k > -1 && !strcmp (basefont_names[k], s))
1041 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1043 @c static void fm_scan_line (MP mp) {
1044 int a, b, c, j, u = 0, v = 0;
1047 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1048 char *p, *q, *r, *s;
1050 switch (mp->ps->mitem->type) {
1054 c = fm_getchar (mp);
1055 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1061 r = mp->ps->mitem->map_line;
1066 if (*r == '\0' || is_cfg_comment (*r))
1068 fm = new_fm_entry (mp);
1069 read_field (r, q, buf);
1070 set_field (tfm_name);
1072 read_field (r, q, buf);
1073 if (*buf != '<' && *buf != '"')
1074 set_field (ps_name);
1076 r = p; /* unget the field */
1077 if (isdigit (*r)) { /* font flags given */
1078 fm->flags = atoi (r);
1079 while (isdigit (*r))
1082 while (1) { /* loop through "specials", encoding, font file */
1087 case '"': /* opening quote */
1092 if (sscanf (r, "%f %n", &d, &j) > 0) {
1093 s = r + j; /* jump behind number, eat also blanks, if any */
1094 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1095 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1096 if (str_prefix (s, "SlantFont")) {
1097 d *= 1000.0; /* correct rounding also for neg. numbers */
1098 fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1099 r = s + strlen ("SlantFont");
1100 } else if (str_prefix (s, "ExtendFont")) {
1102 fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1103 if (fm->extend == 1000)
1105 r = s + strlen ("ExtendFont");
1106 } else { /* unknown name */
1108 *r != ' ' && *r != '"' && *r != '\0';
1109 r++); /* jump over name */
1110 c = *r; /* remember char for temporary end of string */
1112 mp_snprintf(warn_s,128,
1113 "invalid entry for `%s': unknown name `%s' ignored",
1119 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1122 if (*r == '"') /* closing quote */
1125 mp_snprintf(warn_s,128,
1126 "invalid entry for `%s': closing quote missing",
1132 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1133 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1139 default: /* encoding or font file specification */
1143 if (*r == '<' || *r == '[')
1146 read_field (r, q, buf);
1147 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1148 if (strlen (buf) > 4 && strcasecmp (strend (buf) - 4, ".enc") == 0) {
1149 fm->encoding = mp_add_enc (mp, buf);
1150 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1151 } else if (strlen (buf) > 0) { /* file name given */
1152 /* font file, formats:
1153 * subsetting: '<cmr10.pfa'
1154 * no subsetting: '<<cmr10.pfa'
1155 * no embedding: 'cmr10.pfa'
1157 if (a == '<' || u == '<') {
1159 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1161 /* otherwise b == '<' (or '[') => no subsetting */
1163 set_field (ff_name);
1172 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1174 if (is_fontfile (fm)
1175 && strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1177 if (check_fm_entry (mp,fm, true) != 0)
1180 Until here the map line has been completely scanned without errors;
1181 fm points to a valid, freshly filled-out |fm_entry| structure.
1182 Now follows the actual work of registering/deleting.
1184 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
1187 delete_fm_entry (fm);
1191 @c static void fm_read_info (MP mp) {
1194 if (mp->ps->tfm_tree == NULL)
1195 create_avl_trees (mp);
1196 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1198 mp->ps->mitem->lineno = 1;
1199 switch (mp->ps->mitem->type) {
1201 n = mp->ps->mitem->map_line;
1202 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1203 if (!mp->ps->fm_file) {
1204 mp_snprintf(s,256,"cannot open font map file %s",n);
1207 int save_selector = mp->selector;
1208 mp_normalize_selector(mp);
1211 while (!fm_eof ()) {
1213 mp->ps->mitem->lineno++;
1217 mp->selector = save_selector;
1218 mp->ps->fm_file = NULL;
1228 mp->ps->mitem->map_line = NULL; /* done with this line */
1232 @ @c static void init_fm (fm_entry * fm, font_number f) {
1233 if (fm->tfm_num == null_font ) {
1235 fm->tfm_avail = TFM_FOUND;
1240 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1243 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1248 if (mp->ps->tfm_tree == NULL)
1249 fm_read_info (mp); /* only to read default map file */
1250 tfm = mp->font_name[f];
1251 assert (strcmp (tfm, nontfm));
1252 /* Look up for full <tfmname>[+-]<expand> */
1254 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1257 return (fm_entry *) fm;
1259 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1260 if (tfm == NULL) /* not an expanded font, nothing to do */
1264 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1265 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1266 return (fm_entry *) fm; /* font expansion uses the base font */
1271 @ Early check whether a font file exists. Used e. g. for replacing fonts
1272 of embedded PDF files: Without font file, the font within the embedded
1273 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1274 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1275 only once per font file name + expansion parameter. This might help
1276 keeping speed, if many PDF pages with same fonts are to be embedded.
1278 The |ff_tree| contains only font files, which are actually needed,
1279 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1282 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1287 assert (fm->ff_name != NULL);
1288 tmp.ff_name = fm->ff_name;
1289 ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1290 if (ff == NULL) { /* not yet in database */
1291 ff = new_ff_entry (mp);
1292 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1293 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1294 aa = avl_probe (mp->ps->ff_tree, ff);
1295 assert (aa != NULL);
1300 @ Process map file given by its name or map line contents. Items not
1301 beginning with [+-=] flush default map file, if it has not yet been
1302 read. Leading blanks and blanks immediately following [+-=] are ignored.
1305 @c static void mp_process_map_item (MP mp, char *s, int type) {
1309 s++; /* ignore leading blank */
1311 case '+': /* +mapfile.map, +mapline */
1312 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1315 case '=': /* =mapfile.map, =mapline */
1316 mode = FM_REPLACE; /* try to replace earlier entry */
1319 case '-': /* -mapfile.map, -mapline */
1320 mode = FM_DELETE; /* try to delete entry */
1324 mode = FM_DUPIGNORE; /* like +, but also: */
1325 mp_xfree(mp->ps->mitem->map_line);
1326 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1329 s++; /* ignore blank after [+-=] */
1330 p = s; /* map item starts here */
1332 case MAPFILE: /* remove blank at end */
1333 while (*p != '\0' && *p != ' ')
1337 case MAPLINE: /* blank at end allowed */
1342 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1344 if (*s != '\0') { /* only if real item to process */
1345 mp->ps->mitem->mode = mode;
1346 mp->ps->mitem->type = type;
1347 mp->ps->mitem->map_line = s;
1352 @ @<Exported function headers@>=
1353 void mp_map_file (MP mp, str_number t);
1354 void mp_map_line (MP mp, str_number t);
1355 void mp_init_map_file (MP mp, int is_troff);
1358 void mp_map_file (MP mp, str_number t) {
1359 char *s = mp_xstrdup(mp,mp_str (mp,t));
1360 mp_process_map_item (mp, s, MAPFILE);
1363 void mp_map_line (MP mp, str_number t) {
1364 char *s = mp_xstrdup(mp,mp_str (mp,t));
1365 mp_process_map_item (mp, s, MAPLINE);
1370 @c void mp_init_map_file (MP mp, int is_troff) {
1372 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1373 mp->ps->mitem->mode = FM_DUPIGNORE;
1374 mp->ps->mitem->type = MAPFILE;
1375 mp->ps->mitem->map_line = NULL;
1376 r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap);
1379 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1382 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1384 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1389 @ @<Dealloc variables@>=
1390 if (mp->ps->mitem!=NULL) {
1391 mp_xfree(mp->ps->mitem->map_line);
1392 mp_xfree(mp->ps->mitem);
1398 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1401 fm = (fm_entry *) pa;
1402 if (!has_pslink (fm))
1403 delete_fm_entry (fm);
1407 static void destroy_fm_entry_ps (void *pa, void *pb) {
1410 fm = (fm_entry *) pa;
1411 if (!has_tfmlink (fm))
1412 delete_fm_entry (fm);
1416 static void destroy_ff_entry (void *pa, void *pb) {
1419 ff = (ff_entry *) pa;
1420 delete_ff_entry (ff);
1424 static void fm_free (MP mp);
1427 static void fm_free (MP mp) {
1428 if (mp->ps->tfm_tree != NULL)
1429 avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1430 if (mp->ps->ps_tree != NULL)
1431 avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1432 if (mp->ps->ff_tree != NULL)
1433 avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1436 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1437 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1438 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
1439 one or more spaces, a PostScript name, and possibly a space and some other junk.
1440 This routine reads the table, updates |font_ps_name| entries starting after
1441 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
1443 @d ps_tab_name "psfonts.map" /* locates font name translation table */
1446 static void mp_read_psname_table (MP mp) ;
1449 static void mp_read_psname_table (MP mp) {
1451 if (mp->ps->mitem == NULL) {
1452 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1453 mp->ps->mitem->mode = FM_DUPIGNORE;
1454 mp->ps->mitem->type = MAPFILE;
1455 mp->ps->mitem->map_line = NULL;
1457 mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name);
1459 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1460 if (mp_has_fm_entry(mp, k, NULL)) {
1461 mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1464 mp->last_ps_fnum=mp->last_fnum;
1468 @ The traditional function is a lot shorter now.
1472 @* \[44c] Helper functions for Type1 fonts.
1475 typedef char char_entry;
1476 typedef unsigned char Byte;
1480 char_entry *char_ptr, *char_array;
1482 char *job_id_string;
1484 @ @<Set initial...@>=
1485 mp->ps->char_array = NULL;
1486 mp->ps->job_id_string = NULL;
1489 @d SMALL_ARRAY_SIZE 256
1493 void mp_set_job_id (MP mp) {
1494 char *name_string, *format_string, *s;
1496 if (mp->ps->job_id_string != NULL)
1498 if ( mp->job_name==NULL )
1499 mp->job_name = mp_xstrdup(mp,"mpout");
1500 name_string = mp_xstrdup (mp,mp->job_name);
1501 format_string = mp_xstrdup (mp,mp->mem_ident);
1502 slen = SMALL_BUF_SIZE +
1503 strlen (name_string) +
1504 strlen (format_string);
1505 s = mp_xmalloc (mp,slen, sizeof (char));
1506 sprintf (s,"%.4d/%.2d/%.2d %.2d:%.2d %s %s",
1507 (mp->internal[mp_year]>>16),
1508 (mp->internal[mp_month]>>16),
1509 (mp->internal[mp_day]>>16),
1510 (mp->internal[mp_time]>>16) / 60,
1511 (mp->internal[mp_time]>>16) % 60,
1512 name_string, format_string);
1513 mp->ps->job_id_string = mp_xstrdup (mp,s);
1515 mp_xfree (name_string);
1516 mp_xfree (format_string);
1518 static void fnstr_append (MP mp, const char *s) {
1519 size_t l = strlen (s) + 1;
1520 alloc_array (char, l, SMALL_ARRAY_SIZE);
1521 strcat (mp->ps->char_ptr, s);
1522 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1525 @ @<Exported function headers@>=
1526 void mp_set_job_id (MP mp) ;
1528 @ @<Dealloc variables@>=
1529 mp_xfree(mp->ps->job_id_string);
1531 @ this is not really a true crc32, but it should be just enough to keep
1532 subsets prefixes somewhat disjunct
1535 static unsigned long crc32 (int oldcrc, const Byte *buf, int len) {
1536 unsigned long ret = 0;
1539 ret = (23<<24)+(45<<16)+(67<<8)+89;
1542 ret = (ret<<2)+buf[i];
1545 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1546 integer b; /* |char_base[f]| */
1548 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1554 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, int tex_font)
1560 if (mp->ps->job_id_string ==NULL)
1561 mp_fatal_error(mp, "no job id!");
1562 l = strlen (mp->ps->job_id_string) + 1;
1564 alloc_array (char, l, SMALL_ARRAY_SIZE);
1565 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1566 mp->ps->char_ptr = strend (mp->ps->char_array);
1567 if (fm_cur->tfm_name != NULL) {
1568 fnstr_append (mp," TFM name: ");
1569 fnstr_append (mp,fm_cur->tfm_name);
1571 fnstr_append (mp," PS name: ");
1572 if (fm_cur->ps_name != NULL)
1573 fnstr_append (mp,fm_cur->ps_name);
1574 fnstr_append (mp," Encoding: ");
1575 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1576 fnstr_append (mp,(fm_cur->encoding)->file_name);
1578 fnstr_append (mp,"built-in");
1579 fnstr_append (mp," CharSet: ");
1580 for (i = 0; i < 256; i++)
1581 if (mp_char_marked (mp,tex_font, i) && glyph_names[i] != notdef) {
1582 if (glyph_names[i]!=NULL) {
1583 fnstr_append (mp,"/");
1584 fnstr_append (mp,glyph_names[i]);
1587 if (fm_cur->charset != NULL) {
1588 fnstr_append (mp," Extra CharSet: ");
1589 fnstr_append (mp, fm_cur->charset);
1591 crc = crc32 (0L, Z_NULL, 0);
1592 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1593 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1594 * there are 26 uppercase chars ==> each char represents a number in range
1595 * |0..25|. The maximal number that can be represented by the tag is
1596 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1597 * of the CRC must be dropped out.
1599 for (i = 0; i < 6; i++) {
1600 tag[i] = 'A' + crc % 26;
1604 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1610 @d external_enc() (fm_cur->encoding)->glyph_names
1611 @d is_used_char(c) mp_char_marked (mp, tex_font, c)
1612 @d end_last_eexec_line()
1613 mp->ps->hexline_length = HEXLINE_WIDTH;
1615 mp->ps->t1_eexec_encrypt = false
1616 @d t1_log(s) mp_print(mp,(char *)s)
1617 @d t1_putchar(c) wps_chr(c)
1618 @d embed_all_glyphs(tex_font) false
1620 @d extra_charset() mp->ps->dvips_extra_charset
1621 @d update_subset_tag()
1622 @d fixedcontent true
1625 #define PRINTF_BUF_SIZE 1024
1626 char *dvips_extra_charset;
1628 unsigned char *grid;
1629 char *ext_glyph_names[256];
1630 char print_buf[PRINTF_BUF_SIZE];
1631 int t1_byte_waiting;
1633 @ @<Set initial ...@>=
1634 mp->ps->dvips_extra_charset=NULL;
1635 mp->ps->t1_byte_waiting=0;
1638 @d t1_ungetchar(A) mp->ps->t1_byte_waiting=A
1639 @d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file)
1640 @d t1_close() (mp->close_file)(mp,mp->ps->t1_file)
1641 @d valid_code(c) (c >= 0 && c < 256)
1644 int t1_getchar (MP mp) {
1646 unsigned char abyte=0;
1647 void *byte_ptr = &abyte;
1648 if (mp->ps->t1_byte_waiting) {
1649 abyte = mp->ps->t1_byte_waiting;
1650 mp->ps->t1_byte_waiting = 0;
1652 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
1657 @ @<Static variables in the outer block@>=
1658 static const char *standard_glyph_names[256] =
1659 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1660 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1661 notdef, notdef, notdef, notdef, notdef, notdef,
1662 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1663 "space", "exclam", "quotedbl", "numbersign",
1664 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1665 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1666 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1667 "eight", "nine", "colon", "semicolon", "less",
1668 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1669 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1670 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1671 "backslash", "bracketright", "asciicircum", "underscore",
1672 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1673 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1674 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1675 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1676 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1677 notdef, notdef, notdef, notdef, notdef, notdef,
1678 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1679 notdef, notdef, notdef, "exclamdown", "cent",
1680 "sterling", "fraction", "yen", "florin", "section", "currency",
1681 "quotesingle", "quotedblleft", "guillemotleft",
1682 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1683 "dagger", "daggerdbl", "periodcentered", notdef,
1684 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1685 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1686 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1687 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1688 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1689 notdef, notdef, notdef, notdef, notdef, notdef,
1690 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1691 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1692 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1693 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1694 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1695 "germandbls", notdef, notdef, notdef, notdef };
1696 static const char charstringname[] = "/CharStrings";
1699 char **t1_glyph_names;
1700 char *t1_builtin_glyph_names[256];
1701 char charsetstr[0x4000];
1702 boolean read_encoding_only;
1706 #define T1_BUF_SIZE 0x10
1710 #define CS_VMOVETO 4
1711 #define CS_RLINETO 5
1712 #define CS_HLINETO 6
1713 #define CS_VLINETO 7
1714 #define CS_RRCURVETO 8
1715 #define CS_CLOSEPATH 9
1716 #define CS_CALLSUBR 10
1717 #define CS_RETURN 11
1718 #define CS_ESCAPE 12
1720 #define CS_ENDCHAR 14
1721 #define CS_RMOVETO 21
1722 #define CS_HMOVETO 22
1723 #define CS_VHCURVETO 30
1724 #define CS_HVCURVETO 31
1725 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1727 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1728 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1729 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1730 #define CS_SEAC CS_1BYTE_MAX + 6
1731 #define CS_SBW CS_1BYTE_MAX + 7
1732 #define CS_DIV CS_1BYTE_MAX + 12
1733 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1734 #define CS_POP CS_1BYTE_MAX + 17
1735 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1736 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1737 #define CS_MAX CS_2BYTE_MAX
1740 typedef unsigned char byte;
1742 byte nargs; /* number of arguments */
1743 boolean bottom; /* take arguments from bottom of stack? */
1744 boolean clear; /* clear stack? */
1746 } cc_entry; /* CharString Command */
1748 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1750 unsigned short len; /* length of the whole string */
1751 unsigned short cslen; /* length of the encoded part of the string */
1761 unsigned short t1_dr, t1_er;
1762 unsigned short t1_cslen;
1766 typedef char t1_line_entry;
1767 typedef char t1_buf_entry;
1770 t1_line_entry *t1_line_ptr, *t1_line_array;
1771 size_t t1_line_limit;
1772 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1773 size_t t1_buf_limit;
1775 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1776 char *cs_dict_start, *cs_dict_end;
1777 int cs_count, cs_size, cs_size_pos;
1779 char *subr_array_start, *subr_array_end;
1780 int subr_max, subr_size, subr_size_pos;
1782 @ @<Set initial...@>=
1783 mp->ps->t1_line_array = NULL;
1784 mp->ps->t1_buf_array = NULL;
1787 This list contains the begin/end tokens commonly used in the
1788 /Subrs array of a Type 1 font.
1790 @<Static variables in the outer block@>=
1791 static const char *cs_token_pairs_list[][2] = {
1794 {" RD", "noaccess put"},
1795 {" -|", "noaccess put"},
1800 const char **cs_token_pair;
1801 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1802 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1803 long t1_block_length;
1811 @<Set initial ...@>=
1812 mp->ps->hexline_length = 0;
1815 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1816 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1817 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1818 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1819 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1820 @d t1_subrs() t1_prefix("/Subrs")
1821 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1822 @d t1_cleartomark() t1_prefix("cleartomark")
1824 @d isdigit(A) ((A)>='0'&&(A)<='9')
1827 static void end_hexline (MP mp) {
1828 if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
1830 mp->ps->hexline_length = 0;
1833 static void t1_check_pfa (MP mp) {
1834 const int c = t1_getchar (mp);
1835 mp->ps->t1_pfa = (c != 128) ? true : false;
1838 static int t1_getbyte (MP mp)
1840 int c = t1_getchar (mp);
1843 if (mp->ps->t1_block_length == 0) {
1845 mp_fatal_error (mp, "invalid marker");
1846 c = t1_getchar (mp);
1852 mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
1853 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 8;
1854 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 16;
1855 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 24;
1856 c = t1_getchar (mp);
1858 mp->ps->t1_block_length--;
1861 static int hexval (int c) {
1862 if (c >= 'A' && c <= 'F')
1863 return c - 'A' + 10;
1864 else if (c >= 'a' && c <= 'f')
1865 return c - 'a' + 10;
1866 else if (c >= '0' && c <= '9')
1871 static byte edecrypt (MP mp, byte cipher) {
1873 if (mp->ps->t1_pfa) {
1874 while (cipher == 10 || cipher == 13)
1875 cipher = t1_getbyte (mp);
1876 mp->ps->last_hexbyte = cipher = (hexval (cipher) << 4) + hexval (t1_getbyte (mp));
1878 plain = (cipher ^ (mp->ps->t1_dr >> 8));
1879 mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * t1_c1 + t1_c2;
1882 static byte cdecrypt (byte cipher, unsigned short *cr)
1884 const byte plain = (cipher ^ (*cr >> 8));
1885 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1888 static byte eencrypt (MP mp, byte plain)
1890 const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1891 mp->ps->t1_er = (cipher + mp->ps->t1_er) * t1_c1 + t1_c2;
1895 static byte cencrypt (byte plain, unsigned short *cr)
1897 const byte cipher = (plain ^ (*cr >> 8));
1898 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1902 static char *eol (char *s) {
1903 char *p = strend (s);
1904 if (p - s > 1 && p[-1] != 10) {
1910 static float t1_scan_num (MP mp, char *p, char **r)
1915 if (sscanf (p, "%g", &f) != 1) {
1916 remove_eol (p, mp->ps->t1_line_array);
1917 mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1918 mp_fatal_error(mp,s);
1921 for (; isdigit (*p) || *p == '.' ||
1922 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1928 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1931 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1934 while (s1 >= begin_buf && s2 >= s) {
1943 @d alloc_array(T, n, s) do {
1944 if (mp->ps->T##_array == NULL) {
1945 mp->ps->T##_limit = (s);
1946 if ((unsigned)(n) > mp->ps->T##_limit)
1947 mp->ps->T##_limit = (n);
1948 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
1949 mp->ps->T##_ptr = mp->ps->T##_array;
1951 else if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
1952 size_t last_ptr_index;
1953 last_ptr_index = mp->ps->T##_ptr - mp->ps->T##_array;
1954 mp->ps->T##_limit *= 2;
1955 if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
1956 mp->ps->T##_limit = mp->ps->T##_ptr - mp->ps->T##_array + (n);
1957 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
1958 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
1962 @d out_eexec_char(A) t1_outhex(mp,(A))
1965 static void t1_outhex (MP mp, byte b)
1967 static const char *hexdigits = "0123456789ABCDEF";
1968 t1_putchar (hexdigits[b / 16]);
1969 t1_putchar (hexdigits[b % 16]);
1970 mp->ps->hexline_length += 2;
1973 static void t1_getline (MP mp) {
1974 int c, l, eexec_scan;
1976 static const char eexec_str[] = "currentfile eexec";
1977 static int eexec_len = 17; /* |strlen(eexec_str)| */
1980 mp_fatal_error (mp,"unexpected end of file");
1981 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
1982 alloc_array (t1_line, 1, T1_BUF_SIZE);
1983 mp->ps->t1_cslen = 0;
1985 c = t1_getbyte (mp);
1988 while (!t1_eof ()) {
1989 if (mp->ps->t1_in_eexec == 1)
1990 c = edecrypt (mp,c);
1991 alloc_array (t1_line, 1, T1_BUF_SIZE);
1992 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
1993 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
1994 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
1999 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2001 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
2002 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2003 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2004 p = mp->ps->t1_line_ptr - 5;
2007 mp->ps->t1_cslen = l = t1_scan_num (mp, p + 1, 0);
2008 mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2009 /* |mp->ps->cs_start| is an index now */
2010 alloc_array (t1_line, l, T1_BUF_SIZE);
2012 *mp->ps->t1_line_ptr++ = edecrypt (mp,t1_getbyte (mp));
2014 c = t1_getbyte (mp);
2016 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
2017 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2018 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2020 if (eexec_scan == eexec_len)
2021 mp->ps->t1_in_eexec = 1;
2023 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2024 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2025 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2028 static void t1_putline (MP mp)
2030 char *p = mp->ps->t1_line_array;
2031 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2033 if (mp->ps->t1_eexec_encrypt) {
2034 while (p < mp->ps->t1_line_ptr)
2035 out_eexec_char (eencrypt (mp,*p++));
2037 while (p < mp->ps->t1_line_ptr)
2042 static void t1_puts (MP mp, const char *s)
2044 if (s != mp->ps->t1_line_array)
2045 strcpy (mp->ps->t1_line_array, s);
2046 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2050 static void t1_printf (MP mp, const char *fmt, ...)
2053 va_start (args, fmt);
2054 vsprintf (mp->ps->t1_line_array, fmt, args);
2055 t1_puts (mp,mp->ps->t1_line_array);
2059 static void t1_init_params (MP mp, const char *open_name_prefix,
2060 char *cur_file_name) {
2061 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2062 t1_log (open_name_prefix);
2063 t1_log (cur_file_name);
2065 mp->ps->t1_lenIV = 4;
2066 mp->ps->t1_dr = 55665;
2067 mp->ps->t1_er = 55665;
2068 mp->ps->t1_in_eexec = 0;
2069 mp->ps->t1_cs = false;
2070 mp->ps->t1_scan = true;
2071 mp->ps->t1_synthetic = false;
2072 mp->ps->t1_eexec_encrypt = false;
2073 mp->ps->t1_block_length = 0;
2076 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2077 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2078 t1_log (close_name_suffix);
2083 static void t1_check_block_len (MP mp, boolean decrypt) {
2086 if (mp->ps->t1_block_length == 0)
2088 c = t1_getbyte (mp);
2090 c = edecrypt (mp,c);
2091 l = mp->ps->t1_block_length;
2092 if (!(l == 0 && (c == 10 || c == 13))) {
2093 mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2099 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2101 if (!mp->ps->t1_pfa)
2102 t1_check_block_len (mp, false);
2103 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2104 edecrypt (mp, t1_getbyte (mp));
2105 *mp->ps->t1_line_ptr++ = 0;
2107 mp->ps->t1_eexec_encrypt = true;
2108 if (!mp->ps->read_encoding_only)
2109 if (is_included (fm_cur))
2110 t1_putline (mp); /* to put the first four bytes */
2112 static void t1_stop_eexec (MP mp) {
2114 end_last_eexec_line ();
2115 if (!mp->ps->t1_pfa)
2116 t1_check_block_len (mp,true);
2118 c = edecrypt (mp, t1_getbyte (mp));
2119 if (!(c == 10 || c == 13)) {
2120 if (mp->ps->last_hexbyte == 0)
2123 mp_warn (mp,"unexpected data after eexec");
2126 mp->ps->t1_cs = false;
2127 mp->ps->t1_in_eexec = 2;
2129 static void t1_modify_fm (MP mp) {
2130 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2133 static void t1_modify_italic (MP mp) {
2134 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2139 const char *pdfname;
2149 static key_entry font_keys[FONT_KEYS_NUM] = {
2150 {"Ascent", "Ascender", 0, false},
2151 {"CapHeight", "CapHeight", 0, false},
2152 {"Descent", "Descender", 0, false},
2153 {"FontName", "FontName", 0, false},
2154 {"ItalicAngle", "ItalicAngle", 0, false},
2155 {"StemV", "StdVW", 0, false},
2156 {"XHeight", "XHeight", 0, false},
2157 {"FontBBox", "FontBBox", 0, false},
2169 @d ITALIC_ANGLE_CODE 4
2175 @d FONTBBOX4_CODE 10
2176 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2179 static void t1_scan_keys (MP mp, int tex_font,fm_entry *fm_cur) {
2183 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2184 if (t1_prefix ("/FontMatrix")) {
2188 if (t1_prefix ("/ItalicAngle")) {
2189 t1_modify_italic (mp);
2193 if (t1_prefix ("/FontType")) {
2194 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2195 if ((i = t1_scan_num (mp,p, 0)) != 1) {
2197 mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2198 mp_fatal_error(mp,s);
2202 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2203 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2205 if (key - font_keys == MAX_KEY_CODE)
2208 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2210 if ((k = key - font_keys) == FONTNAME_CODE) {
2213 remove_eol (p, mp->ps->t1_line_array);
2214 mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2215 mp_fatal_error(mp,s);
2217 r = ++p; /* skip the slash */
2218 if (is_included (fm_cur)) {
2219 /* save the fontname */
2220 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2221 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2222 mp->ps->fontname_buf[i]=0;
2224 if(is_subsetted (fm_cur)) {
2225 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2226 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2228 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2230 alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
2232 strncpy (r, fm_cur->subset_tag , 6);
2234 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2235 mp->ps->t1_line_ptr = eol (r);
2237 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2239 mp->ps->t1_line_ptr = eol (r);
2244 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2245 && (*p == '[' || *p == '{'))
2247 if (k == FONTBBOX1_CODE) {
2248 for (i = 0; i < 4; i++) {
2249 key[i].value = t1_scan_num (mp, p, &r);
2254 key->value = t1_scan_num (mp, p, 0);
2256 static void t1_scan_param (MP mp, int tex_font,fm_entry *fm_cur)
2258 static const char *lenIV = "/lenIV";
2259 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2261 if (t1_prefix (lenIV)) {
2262 mp->ps->t1_lenIV = t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2265 t1_scan_keys (mp, tex_font,fm_cur);
2267 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2268 if (glyph_names[b] != notdef) {
2269 mp_xfree (glyph_names[b]);
2270 glyph_names[b] = (char *) notdef;
2272 if (glyph_names[a] != notdef) {
2273 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2276 static void t1_builtin_enc (MP mp) {
2277 int i, a, b, c, counter = 0;
2280 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2282 if (t1_suffix ("def")) { /* predefined encoding */
2283 sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2284 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2285 for (i = 0; i < 256; i++)
2286 if (standard_glyph_names[i] == notdef)
2287 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2289 mp->ps->t1_builtin_glyph_names[i] =
2290 mp_xstrdup (mp,standard_glyph_names[i]);
2291 mp->ps->t1_encoding = ENC_STANDARD;
2294 mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2295 mp->ps->t1_buf_array);
2296 mp_fatal_error(mp,s);
2300 mp->ps->t1_encoding = ENC_BUILTIN;
2302 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2303 * not a predefined encoding
2305 * We have two possible forms of Encoding vector. The first case is
2307 * /Encoding [/a /b /c...] readonly def
2309 * and the second case can look like
2311 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2317 for (i = 0; i < 256; i++)
2318 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2319 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2320 r = strchr (mp->ps->t1_line_array, '[') + 1;
2324 for (p = mp->ps->t1_buf_array, r++;
2325 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2329 if (counter > 255) {
2331 (mp, "encoding vector contains more than 256 names");
2333 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2334 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2337 if (*r != 10 && *r != '%') {
2338 if (str_prefix (r, "] def")
2339 || str_prefix (r, "] readonly def"))
2343 remove_eol (r, mp->ps->t1_line_array);
2344 mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2345 mp->ps->t1_line_array);
2346 mp_fatal_error(mp,s);
2350 r = mp->ps->t1_line_array;
2352 } else { /* the second case */
2353 p = strchr (mp->ps->t1_line_array, 10);
2357 p = mp->ps->t1_line_array;
2360 check for `dup <index> <glyph> put'
2362 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2363 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2364 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2365 mp->ps->t1_builtin_glyph_names[i] =
2366 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2367 p = strstr (p, " put") + strlen (" put");
2371 check for `dup dup <to> exch <from> get put'
2373 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2374 && valid_code (a) && valid_code (b)) {
2375 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2376 p = strstr (p, " get put") + strlen (" get put");
2380 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2383 (p, "dup dup %i %i getinterval %i exch putinterval",
2384 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2385 && valid_code (c)) {
2386 for (i = 0; i < c; i++)
2387 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2388 p = strstr (p, " putinterval") + strlen (" putinterval");
2392 check for `def' or `readonly def'
2394 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2395 && strcmp (p, "def\n") == 0)
2398 skip an unrecognizable word
2401 while (*p != ' ' && *p != 10)
2409 static void t1_check_end (MP mp) {
2413 if (t1_prefix ("{restore}"))
2419 char *ff_name; /* base name of font file */
2420 char *ff_path; /* full path to font file */
2424 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2426 ff = check_ff_exist (mp, fm_cur);
2427 if (ff->ff_path != NULL) {
2428 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font);
2430 mp_warn (mp, "cannot open Type 1 font file for reading");
2433 t1_init_params (mp,open_name_prefix,fm_cur->ff_name);
2434 mp->ps->fontfile_found = true;
2438 static void t1_scan_only (MP mp, int tex_font, fm_entry *fm_cur) {
2441 t1_scan_param (mp,tex_font, fm_cur);
2443 while (mp->ps->t1_in_eexec == 0);
2444 t1_start_eexec (mp,fm_cur);
2447 t1_scan_param (mp,tex_font, fm_cur);
2449 while (!(t1_charstrings () || t1_subrs ()));
2452 static void t1_include (MP mp, int tex_font, fm_entry *fm_cur) {
2455 t1_scan_param (mp,tex_font, fm_cur);
2458 while (mp->ps->t1_in_eexec == 0);
2459 t1_start_eexec (mp,fm_cur);
2462 t1_scan_param (mp,tex_font, fm_cur);
2465 while (!(t1_charstrings () || t1_subrs ()));
2466 mp->ps->t1_cs = true;
2471 while (!t1_end_eexec ());
2473 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2478 while (!t1_cleartomark ());
2479 t1_check_end (mp); /* write "{restore}if" if found */
2484 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2486 mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2487 mp_fatal_error(mp,s);
2491 static const char **check_cs_token_pair (MP mp) {
2492 const char **p = (const char **) cs_token_pairs_list;
2493 for (; p[0] != NULL; ++p)
2494 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2499 static void cs_store (MP mp, boolean is_subr) {
2503 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2504 *mp->ps->t1_buf_ptr++ = *p++);
2505 *mp->ps->t1_buf_ptr = 0;
2507 subr = t1_scan_num (mp, p + 1, 0);
2509 ptr = mp->ps->subr_tab + subr;
2511 ptr = mp->ps->cs_ptr++;
2512 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2514 mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2515 mp_fatal_error(mp,s);
2517 if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
2518 ptr->glyph_name = (char *) notdef;
2520 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2522 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2523 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2524 (unsigned) (mp->ps->t1_cslen + 4));
2525 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2526 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2527 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2528 *mp->ps->t1_buf_ptr++ = 10;
2529 if (is_subr && mp->ps->cs_token_pair == NULL)
2530 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2531 ptr->len = mp->ps->t1_buf_ptr - mp->ps->t1_buf_array;
2532 ptr->cslen = mp->ps->t1_cslen;
2533 ptr->data = mp_xmalloc (mp,ptr->len , sizeof (byte));
2534 memcpy (ptr->data, mp->ps->t1_buf_array, ptr->len);
2538 #define store_subr(mp) cs_store(mp,true)
2539 #define store_cs(mp) cs_store(mp,false)
2541 #define CC_STACK_SIZE 24
2543 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2544 static cc_entry cc_tab[CS_MAX];
2545 static boolean is_cc_init = false;
2549 if (stack_ptr - cc_stack < (N)) \
2553 #define stack_error(N) { \
2555 mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2556 (int) N, (int)(stack_ptr - cc_stack)); \
2562 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2564 #define cc_push(V) *stack_ptr++ = V
2565 #define cc_clear() stack_ptr = cc_stack
2567 #define set_cc(N, B, A, C) \
2568 cc_tab[N].nargs = A; \
2569 cc_tab[N].bottom = B; \
2570 cc_tab[N].clear = C; \
2571 cc_tab[N].valid = true
2573 static void cc_init (void) {
2577 for (i = 0; i < CS_MAX; i++)
2578 cc_tab[i].valid = false;
2579 set_cc (CS_HSTEM, true, 2, true);
2580 set_cc (CS_VSTEM, true, 2, true);
2581 set_cc (CS_VMOVETO, true, 1, true);
2582 set_cc (CS_RLINETO, true, 2, true);
2583 set_cc (CS_HLINETO, true, 1, true);
2584 set_cc (CS_VLINETO, true, 1, true);
2585 set_cc (CS_RRCURVETO, true, 6, true);
2586 set_cc (CS_CLOSEPATH, false, 0, true);
2587 set_cc (CS_CALLSUBR, false, 1, false);
2588 set_cc (CS_RETURN, false, 0, false);
2590 |set_cc(CS_ESCAPE, false, 0, false);|
2592 set_cc (CS_HSBW, true, 2, true);
2593 set_cc (CS_ENDCHAR, false, 0, true);
2594 set_cc (CS_RMOVETO, true, 2, true);
2595 set_cc (CS_HMOVETO, true, 1, true);
2596 set_cc (CS_VHCURVETO, true, 4, true);
2597 set_cc (CS_HVCURVETO, true, 4, true);
2598 set_cc (CS_DOTSECTION, false, 0, true);
2599 set_cc (CS_VSTEM3, true, 6, true);
2600 set_cc (CS_HSTEM3, true, 6, true);
2601 set_cc (CS_SEAC, true, 5, true);
2602 set_cc (CS_SBW, true, 4, true);
2603 set_cc (CS_DIV, false, 2, false);
2604 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2605 set_cc (CS_POP, false, 0, false);
2606 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2612 @d cs_getchar(mp) cdecrypt(*data++, &cr)
2614 @d mark_subr(mp,n) cs_mark(mp,0, n)
2615 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2616 @d SMALL_BUF_SIZE 256
2619 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2620 char buf[SMALL_BUF_SIZE];
2623 va_start (args, fmt);
2624 vsprintf (buf, fmt, args);
2626 if (cs_name == NULL) {
2627 mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2629 mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2634 static void cs_mark (MP mp, const char *cs_name, int subr)
2640 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2644 if (cs_name == NULL) {
2646 ptr = mp->ps->subr_tab + subr;
2650 if (mp->ps->cs_notdef != NULL &&
2651 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2652 ptr = mp->ps->cs_notdef;
2654 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2655 if (strcmp (ptr->glyph_name, cs_name) == 0)
2657 if (ptr == mp->ps->cs_ptr) {
2659 mp_snprintf (s,128,"glyph `%s' undefined", cs_name);
2663 if (ptr->glyph_name == notdef)
2664 mp->ps->cs_notdef = ptr;
2667 /* only marked CharString entries and invalid entries can be skipped;
2668 valid marked subrs must be parsed to keep the stack in sync */
2669 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2671 ptr->is_used = true;
2673 cs_len = ptr->cslen;
2674 data = ptr->data + 4;
2675 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2677 while (cs_len > 0) {
2679 b = cs_getchar (mp);
2683 else if (b <= 250) {
2685 a = ((b - 247) << 8) + 108 + cs_getchar (mp);
2686 } else if (b <= 254) {
2688 a = -((b - 251) << 8) - 108 - cs_getchar (mp);
2691 a = (cs_getchar (mp) & 0xff) << 24;
2692 a |= (cs_getchar (mp) & 0xff) << 16;
2693 a |= (cs_getchar (mp) & 0xff) << 8;
2694 a |= (cs_getchar (mp) & 0xff) << 0;
2695 if (sizeof (integer) > 4 && (a & 0x80000000))
2700 if (b == CS_ESCAPE) {
2701 b = cs_getchar (mp) + CS_1BYTE_MAX;
2705 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2711 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2715 if (stack_ptr - cc_stack < cc->nargs)
2716 cs_warn (mp,cs_name, subr,
2717 "less arguments on stack (%i) than required (%i)",
2718 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2719 else if (stack_ptr - cc_stack > cc->nargs)
2720 cs_warn (mp,cs_name, subr,
2721 "more arguments on stack (%i) than required (%i)",
2722 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2724 switch (cc - cc_tab) {
2729 if (!mp->ps->subr_tab[a1].valid) {
2730 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2738 case CS_CALLOTHERSUBR:
2739 if (cc_get (-1) == 3)
2740 lastargOtherSubr3 = cc_get (-3);
2741 a1 = cc_get (-2) + 2;
2745 cc_push (lastargOtherSubr3);
2746 /* the only case when we care about the value being pushed onto
2747 stack is when POP follows CALLOTHERSUBR (changing hints by
2755 mark_cs (mp,standard_glyph_names[a1]);
2756 mark_cs (mp,standard_glyph_names[a2]);
2765 cs_error: /* an error occured during parsing */
2768 ptr->is_used = false;
2771 static void t1_subset_ascii_part (MP mp, int tex_font, fm_entry *fm_cur)
2775 while (!t1_prefix ("/Encoding")) {
2776 t1_scan_param (mp,tex_font, fm_cur);
2780 t1_builtin_enc (mp);
2781 if (is_reencoded (fm_cur))
2782 mp->ps->t1_glyph_names = external_enc ();
2784 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2786 |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2787 make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2788 update_subset_tag ();
2791 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2792 t1_puts (mp,"/Encoding StandardEncoding def\n");
2795 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2796 for (i = 0, j = 0; i < 256; i++) {
2797 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2799 t1_printf (mp,"dup %i /%s put\n", (int) t1_char (i),
2800 mp->ps->t1_glyph_names[i]);
2803 /* We didn't mark anything for the Encoding array. */
2804 /* We add "dup 0 /.notdef put" for compatibility */
2805 /* with Acrobat 5.0. */
2807 t1_puts (mp,"dup 0 /.notdef put\n");
2808 t1_puts (mp,"readonly def\n");
2812 t1_scan_param (mp,tex_font, fm_cur);
2813 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
2816 while (mp->ps->t1_in_eexec == 0);
2819 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
2820 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
2822 static void cs_init (MP mp) {
2823 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2824 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2825 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2826 mp->ps->cs_token_pair = NULL;
2827 mp->ps->subr_tab = NULL;
2828 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2829 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2832 static void init_cs_entry ( cs_entry * cs) {
2834 cs->glyph_name = NULL;
2837 cs->is_used = false;
2841 static void t1_mark_glyphs (MP mp, int tex_font);
2843 static void t1_read_subrs (MP mp, int tex_font, fm_entry *fm_cur)
2848 while (!(t1_charstrings () || t1_subrs ())) {
2849 t1_scan_param (mp,tex_font, fm_cur);
2854 mp->ps->t1_cs = true;
2855 mp->ps->t1_scan = false;
2858 mp->ps->subr_size_pos = strlen ("/Subrs") + 1;
2859 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2860 mp->ps->subr_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2861 if (mp->ps->subr_size == 0) {
2862 while (!t1_charstrings ())
2866 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
2867 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,mp->ps->subr_size, sizeof (cs_entry));
2868 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2869 init_cs_entry (ptr);
2870 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2872 while (mp->ps->t1_cslen) {
2876 /* mark the first four entries without parsing */
2877 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2878 mp->ps->subr_tab[i].is_used = true;
2879 /* the end of the Subrs array might have more than one line so we need to
2880 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2881 the Subrs array followed by the CharStrings dict immediately (synthetic
2882 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2883 we will treat the font as synthetic and ignore everything until next
2886 #define POST_SUBRS_SCAN 5
2888 *mp->ps->t1_buf_array = 0;
2889 for (i = 0; i < POST_SUBRS_SCAN; i++) {
2890 if (t1_charstrings ())
2892 s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2893 alloc_array (t1_buf, s, T1_BUF_SIZE);
2894 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2897 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2898 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2899 suppose synthetic font */
2900 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2902 mp_xfree (ptr->data);
2903 mp_xfree (mp->ps->subr_tab);
2904 mp_xfree (mp->ps->subr_array_start);
2905 mp_xfree (mp->ps->subr_array_end);
2907 mp->ps->t1_cs = false;
2908 mp->ps->t1_synthetic = true;
2909 while (!(t1_charstrings () || t1_subrs ()))
2916 static void t1_flush_cs (MP mp, boolean is_subr)
2919 byte *r, *return_cs = NULL;
2920 cs_entry *tab, *end_tab, *ptr;
2921 char *start_line, *line_end;
2922 int count, size_pos;
2923 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2925 start_line = mp->ps->subr_array_start;
2926 line_end = mp->ps->subr_array_end;
2927 size_pos = mp->ps->subr_size_pos;
2928 tab = mp->ps->subr_tab;
2929 count = mp->ps->subr_max + 1;
2930 end_tab = mp->ps->subr_tab + count;
2932 start_line = mp->ps->cs_dict_start;
2933 line_end = mp->ps->cs_dict_end;
2934 size_pos = mp->ps->cs_size_pos;
2935 tab = mp->ps->cs_tab;
2936 end_tab = mp->ps->cs_ptr;
2937 count = mp->ps->cs_count;
2939 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2940 for (p = start_line; p - start_line < size_pos;)
2941 *mp->ps->t1_line_ptr++ = *p++;
2942 while (isdigit (*p))
2944 sprintf (mp->ps->t1_line_ptr, "%u", count);
2945 strcat (mp->ps->t1_line_ptr, p);
2946 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2949 /* create |return_cs| to replace unsused subr's */
2953 return_cs = mp_xmalloc (mp, (mp->ps->t1_lenIV + 1) , sizeof(byte));
2954 if ( mp->ps->t1_lenIV > 0) {
2955 for (cs_len = 0, r = return_cs; cs_len < mp->ps->t1_lenIV; cs_len++, r++)
2956 *r = cencrypt (0x00, &cr);
2957 *r = cencrypt (CS_RETURN, &cr);
2959 *return_cs = CS_RETURN;
2964 for (ptr = tab; ptr < end_tab; ptr++) {
2967 sprintf (mp->ps->t1_line_array, "dup %i %u", (int) (ptr - tab),
2970 sprintf (mp->ps->t1_line_array, "/%s %u", ptr->glyph_name, ptr->cslen);
2971 p = strend (mp->ps->t1_line_array);
2972 memcpy (p, ptr->data, ptr->len);
2973 mp->ps->t1_line_ptr = p + ptr->len;
2976 /* replace unsused subr's by |return_cs| */
2978 sprintf (mp->ps->t1_line_array, "dup %i %u%s ", (int) (ptr - tab),
2979 cs_len, mp->ps->cs_token_pair[0]);
2980 p = strend (mp->ps->t1_line_array);
2981 memcpy (p, return_cs, cs_len);
2982 mp->ps->t1_line_ptr = p + cs_len;
2984 sprintf (mp->ps->t1_line_array, " %s", mp->ps->cs_token_pair[1]);
2985 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2989 mp_xfree (ptr->data);
2990 if (ptr->glyph_name != notdef)
2991 mp_xfree (ptr->glyph_name);
2993 sprintf (mp->ps->t1_line_array, "%s", line_end);
2994 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2997 mp_xfree (return_cs);
2999 mp_xfree (start_line);
3000 mp_xfree (line_end);
3003 static void t1_mark_glyphs (MP mp, int tex_font)
3006 char *charset = extra_charset ();
3009 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
3010 if (mp->ps->cs_tab != NULL)
3011 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3013 ptr->is_used = true;
3014 if (mp->ps->subr_tab != NULL) {
3015 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3017 ptr->is_used = true;
3018 mp->ps->subr_max = mp->ps->subr_size - 1;
3022 mark_cs (mp,notdef);
3023 for (i = 0; i < 256; i++)
3024 if (is_used_char (i)) {
3025 if (mp->ps->t1_glyph_names[i] == notdef) {
3027 mp_snprintf(S,128, "character %i is mapped to %s", i, notdef);
3030 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3032 if (charset == NULL)
3034 g = s = charset + 1; /* skip the first '/' */
3037 while (*s != '/' && s < r)
3039 *s = 0; /* terminate g by rewriting '/' to 0 */
3044 if (mp->ps->subr_tab != NULL)
3045 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3046 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3048 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3049 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3052 static void t1_subset_charstrings (MP mp, int tex_font)
3055 mp->ps->cs_size_pos =
3056 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3057 - mp->ps->t1_line_array + 1;
3058 /* |cs_size_pos| points to the number indicating
3059 dict size after "/CharStrings" */
3060 mp->ps->cs_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3061 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,mp->ps->cs_size, sizeof(cs_entry));
3062 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3063 init_cs_entry (ptr);
3064 mp->ps->cs_notdef = NULL;
3065 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3067 while (mp->ps->t1_cslen) {
3071 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3072 t1_mark_glyphs (mp,tex_font);
3073 if (mp->ps->subr_tab != NULL) {
3074 if (mp->ps->cs_token_pair == NULL)
3076 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3079 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3085 static void t1_subset_end (MP mp)
3087 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3088 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3092 while (!t1_end_eexec ())
3093 t1_getline (mp); /* ignore the rest */
3094 t1_putline (mp); /* write "mark currentfile closefile" */
3096 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3101 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3102 while (!t1_cleartomark ()) {
3106 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3107 t1_check_end (mp); /* write "{restore}if" if found */
3111 static int t1_updatefm (MP mp, int f, fm_entry *fm)
3114 mp->ps->read_encoding_only = true;
3115 if (!t1_open_fontfile (mp,fm,NULL)) {
3118 t1_scan_only (mp,f, fm);
3119 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3121 while (*p != ' ' && *p != 0)
3125 t1_close_font_file (mp,"");
3130 static void writet1 (MP mp, int tex_font, fm_entry *fm_cur) {
3131 int save_selector = mp->selector;
3132 mp_normalize_selector(mp);
3133 mp->ps->read_encoding_only = false;
3134 if (!is_included (fm_cur)) { /* scan parameters from font file */
3135 if (!t1_open_fontfile (mp,fm_cur,"{"))
3137 t1_scan_only (mp,tex_font, fm_cur);
3138 t1_close_font_file (mp,"}");
3141 if (!is_subsetted (fm_cur)) { /* include entire font */
3142 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3144 t1_include (mp,tex_font,fm_cur);
3145 t1_close_font_file (mp,">>");
3148 /* partial downloading */
3149 if (!t1_open_fontfile (mp,fm_cur,"<"))
3151 t1_subset_ascii_part (mp,tex_font,fm_cur);
3152 t1_start_eexec (mp,fm_cur);
3155 t1_read_subrs (mp,tex_font, fm_cur);
3156 t1_subset_charstrings (mp,tex_font);
3158 t1_close_font_file (mp,">");
3159 mp->selector = save_selector;
3163 static void t1_free (MP mp);
3166 static void t1_free (MP mp) {
3167 mp_xfree (mp->ps->t1_line_array);
3168 mp_xfree (mp->ps->t1_buf_array);
3172 @* \[44d] Embedding fonts.
3174 @ The |tfm_num| is officially of type |font_number|, but that
3175 type does not exist yet at this point in the output order.
3179 char *tfm_name; /* TFM file name */
3180 char *ps_name; /* PostScript name */
3181 integer flags; /* font flags */
3182 char *ff_name; /* font file name */
3183 char *subset_tag; /* pseudoUniqueTag for subsetted font */
3184 enc_entry *encoding; /* pointer to corresponding encoding */
3185 unsigned int tfm_num; /* number of the TFM refering this entry */
3186 unsigned short type; /* font type (T1/TTF/...) */
3187 short slant; /* SlantFont */
3188 short extend; /* ExtendFont */
3189 integer ff_objnum; /* FontFile object number */
3190 integer fn_objnum; /* FontName/BaseName object number */
3191 integer fd_objnum; /* FontDescriptor object number */
3192 char *charset; /* string containing used glyphs */
3193 boolean all_glyphs; /* embed all glyphs? */
3194 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
3195 short tfm_avail; /* flags whether a tfm is available */
3196 short pid; /* Pid for truetype fonts */
3197 short eid; /* Eid for truetype fonts */
3203 #define FONTNAME_BUF_SIZE 128
3204 boolean fontfile_found;
3205 boolean is_otf_font;
3206 char fontname_buf[FONTNAME_BUF_SIZE];
3214 @d set_included(fm) ((fm)->type |= F_INCLUDED)
3215 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
3216 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
3217 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
3219 @d is_included(fm) ((fm)->type & F_INCLUDED)
3220 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
3221 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
3222 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
3223 @d is_reencoded(fm) ((fm)->encoding != NULL)
3224 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
3225 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
3227 @d fm_slant(fm) (fm)->slant
3228 @d fm_extend(fm) (fm)->extend
3229 @d fm_fontfile(fm) (fm)->ff_name
3232 static boolean mp_font_is_reencoded (MP mp, int f);
3233 static boolean mp_font_is_included (MP mp, int f);
3234 static boolean mp_font_is_subsetted (MP mp, int f);
3237 static boolean mp_font_is_reencoded (MP mp, int f) {
3239 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3241 && (fm->ps_name != NULL)
3242 && is_reencoded (fm))
3247 static boolean mp_font_is_included (MP mp, int f) {
3249 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3251 && (fm->ps_name != NULL && fm->ff_name != NULL)
3252 && is_included (fm))
3257 static boolean mp_font_is_subsetted (MP mp, int f) {
3259 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
3261 && (fm->ps_name != NULL && fm->ff_name != NULL)
3262 && is_included (fm) && is_subsetted (fm))
3268 @ @<Exported function headers@>=
3269 char * mp_fm_encoding_name (MP mp, int f);
3270 char * mp_fm_font_name (MP mp, int f);
3273 static char * mp_fm_font_subset_name (MP mp, int f);
3276 @c char * mp_fm_encoding_name (MP mp, int f) {
3279 if (mp_has_fm_entry (mp, f, &fm)) {
3280 if (fm != NULL && (fm->ps_name != NULL)) {
3281 if (is_reencoded (fm)) {
3283 if (e->enc_name!=NULL)
3284 return mp_xstrdup(mp,e->enc_name);
3290 print_err ("fontmap encoding problems for font ");
3291 mp_print(mp,mp->font_name[f]);
3295 char * mp_fm_font_name (MP mp, int f) {
3297 if (mp_has_fm_entry (mp, f,&fm)) {
3298 if (fm != NULL && (fm->ps_name != NULL)) {
3299 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3300 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3301 if (t1_updatefm(mp,f,fm)) {
3302 mp->font_ps_name_fixed[f] = true;
3304 print_err ("font loading problems for font ");
3305 mp_print(mp,mp->font_name[f]);
3309 return mp_xstrdup(mp,fm->ps_name);
3312 print_err ("fontmap name problems for font ");
3313 mp_print(mp,mp->font_name[f]);
3318 static char * mp_fm_font_subset_name (MP mp, int f) {
3320 if (mp_has_fm_entry (mp, f, &fm)) {
3321 if (fm != NULL && (fm->ps_name != NULL)) {
3322 if (is_subsetted(fm)) {
3323 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3324 mp_snprintf(s,strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3327 return mp_xstrdup(mp,fm->ps_name);
3331 print_err ("fontmap name problems for font ");
3332 mp_print(mp,mp->font_name[f]);
3338 static integer mp_fm_font_slant (MP mp, int f);
3339 static integer mp_fm_font_extend (MP mp, int f);
3342 @c static integer mp_fm_font_slant (MP mp, int f) {
3344 if (mp_has_fm_entry (mp, f, &fm)) {
3345 if (fm != NULL && (fm->ps_name != NULL)) {
3351 static integer mp_fm_font_extend (MP mp, int f) {
3353 if (mp_has_fm_entry (mp, f, &fm)) {
3354 if (fm != NULL && (fm->ps_name != NULL)) {
3362 static boolean mp_do_ps_font (MP mp, font_number f);
3364 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3366 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3369 if (is_truetype(fm_cur) ||
3370 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3373 if (is_included(fm_cur)) {
3374 mp_ps_print_nl(mp,"%%BeginResource: font ");
3375 if (is_subsetted(fm_cur)) {
3376 mp_ps_print(mp, fm_cur->subset_tag);
3377 mp_ps_print_char(mp,'-');
3379 mp_ps_print(mp, fm_cur->ps_name);
3381 writet1 (mp,f,fm_cur);
3382 mp_ps_print_nl(mp,"%%EndResource");
3388 @ Included subset fonts do not need and encoding vector, make
3389 sure we skip that case.
3392 static void mp_list_used_resources (MP mp, int prologues, int procset);
3394 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
3395 font_number f; /* fonts used in a text node or as loop counters */
3396 int ff; /* a loop counter */
3397 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3400 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
3402 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3405 for (f=null_font+1;f<=mp->last_fnum;f++) {
3406 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3407 for (ff=ldf;ff>=null_font;ff--) {
3408 if ( mp_has_font_size(mp,ff) )
3409 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3412 if ( mp_font_is_subsetted(mp,f) )
3414 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
3415 (unsigned)mp->max_print_line )
3416 mp_ps_print_nl(mp, "%%+ encoding");
3419 mp_ps_print_nl(mp, "%%+ encoding");
3421 mp_ps_print_char(mp, ' ');
3422 mp_ps_print(mp, mp->font_enc_name[f]);
3430 for (f=null_font+1;f<=mp->last_fnum;f++) {
3431 if ( mp_has_font_size(mp,f) ) {
3432 for (ff=ldf;ff>=null_font;ff--) {
3433 if ( mp_has_font_size(mp,ff) )
3434 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3437 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
3438 (unsigned)mp->max_print_line )
3439 mp_ps_print_nl(mp, "%%+ font");
3442 mp_ps_print_nl(mp, "%%+ font");
3444 mp_ps_print_char(mp, ' ');
3445 if ( (prologues==3)&&
3446 (mp_font_is_subsetted(mp,f)) )
3447 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3449 mp_ps_print(mp, mp->font_ps_name[f]);
3459 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
3461 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
3462 font_number f; /* fonts used in a text node or as loop counters */
3463 int ff; /* a loop counter */
3464 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3467 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3469 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3472 for (f=null_font+1;f<=mp->last_fnum;f++) {
3473 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3474 for (ff=ldf;ff>= null_font;ff++) {
3475 if ( mp_has_font_size(mp,ff) )
3476 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3479 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3481 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(unsigned)mp->max_print_line )
3482 mp_ps_print_nl(mp, "%%+ encoding");
3485 mp_ps_print_nl(mp, "%%+ encoding");
3487 mp_ps_print_char(mp, ' ');
3488 mp_ps_print(mp, mp->font_enc_name[f]);
3497 for (f=null_font+1;f<=mp->last_fnum;f++) {
3498 if ( mp_has_font_size(mp,f) ) {
3499 for (ff=ldf;ff>= null_font;ff--) {
3500 if ( mp_has_font_size(mp,ff) )
3501 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3504 if ( ! mp_font_is_included(mp,f) )
3506 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3507 mp_ps_print_nl(mp, "%%+ font");
3510 mp_ps_print_nl(mp, "%%+ font");
3512 mp_ps_print_char(mp, ' ');
3513 if ( mp_font_is_subsetted(mp,f) )
3514 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3516 mp_ps_print(mp, mp->font_ps_name[f]);
3526 @ @<Declarations...@>=
3527 static void mp_list_needed_resources (MP mp, int prologues);
3529 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
3530 font_number f; /* fonts used in a text node or as loop counters */
3531 int ff; /* a loop counter */
3532 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3536 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3537 if ( mp_has_font_size(mp,f)) {
3538 for (ff=ldf;ff>=null_font;ff--) {
3539 if ( mp_has_font_size(mp,ff) )
3540 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3543 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3545 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3546 mp_ps_print_nl(mp, "%%+ font");
3549 mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
3551 mp_ps_print_char(mp, ' ');
3552 mp_ps_print(mp, mp->font_ps_name[f]);
3558 if ( ! firstitem ) {
3562 for (f=null_font+1;f<= mp->last_fnum;f++) {
3563 if ( mp_has_font_size(mp,f) ) {
3564 for (ff=ldf;ff>=null_font;ff-- ) {
3565 if ( mp_has_font_size(mp,ff) )
3566 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3569 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3571 mp_ps_print(mp, "%%IncludeResource: font ");
3572 mp_ps_print(mp, mp->font_ps_name[f]);
3583 static void mp_write_font_definition (MP mp, font_number f, int prologues);
3587 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3588 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3590 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
3591 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3592 (mp_fm_font_extend(mp,f)!=0)||
3593 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3594 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3595 if ( (mp_font_is_subsetted(mp,f))&&
3596 (mp_font_is_included(mp,f))&&(prologues==3))
3597 mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3599 mp_ps_name_out(mp, mp->font_ps_name[f],true);
3600 mp_ps_print(mp, " fcp");
3602 if ( applied_reencoding(f) ) {
3603 mp_ps_print(mp, "/Encoding ");
3604 mp_ps_print(mp, mp->font_enc_name[f]);
3605 mp_ps_print(mp, " def ");
3607 if ( mp_fm_font_slant(mp,f)!=0 ) {
3608 mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
3609 mp_ps_print(mp, " SlantFont ");
3611 if ( mp_fm_font_extend(mp,f)!=0 ) {
3612 mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3613 mp_ps_print(mp, " ExtendFont ");
3615 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3616 mp_ps_print(mp, " 890 ScaleFont ");
3617 mp_ps_print(mp, " 277 SlantFont ");
3619 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3620 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3621 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3622 mp_ps_print(mp, "/space[0 -278]def ");
3623 mp_ps_print(mp, "/a12[-904 -939]def ");
3624 mp_ps_print(mp, "end def ");
3626 mp_ps_print(mp, "currentdict end");
3628 mp_ps_print_defined_name(mp,f,prologues);
3629 mp_ps_print(mp, " exch definefont pop");
3635 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
3638 @c static void mp_ps_print_defined_name(MP mp, font_number A, int prologues) {
3639 mp_ps_print(mp, " /");
3640 if ((mp_font_is_subsetted(mp,(A)))&&
3641 (mp_font_is_included(mp,(A)))&&(prologues==3))
3642 mp_ps_print(mp, mp_fm_font_subset_name(mp,(A)));
3644 mp_ps_print(mp, mp->font_ps_name[(A)]);
3645 if ( mp_xstrcmp(mp->font_name[(A)],"psyrgo")==0 )
3646 mp_ps_print(mp, "-Slanted");
3647 if ( mp_xstrcmp(mp->font_name[(A)],"zpzdr-reversed")==0 )
3648 mp_ps_print(mp, "-Reverse");
3649 if ( applied_reencoding((A)) ) {
3650 mp_ps_print(mp, "-");
3651 mp_ps_print(mp, mp->font_enc_name[(A)]);
3653 if ( mp_fm_font_slant(mp,(A))!=0 ) {
3654 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,(A))) ;
3656 if ( mp_fm_font_extend(mp,(A))!=0 ) {
3657 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,(A)));
3661 @ @<Include encodings and fonts for edge structure~|h|@>=
3662 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
3663 @<Embed fonts that are available@>
3665 @ @<Embed fonts that are available@>=
3668 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3671 for (f=null_font+1;f<=mp->last_fnum;f++) {
3672 if ( cur_fsize[f]!=null ) {
3673 if (prologues==3 ) {
3674 if ( ! mp_do_ps_font(mp,f) ) {
3675 if ( mp_has_fm_entry(mp,f, NULL) ) {
3676 print_err("Font embedding failed");
3681 cur_fsize[f]=link(cur_fsize[f]);
3682 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3686 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3688 } while (! done_fonts);
3691 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3694 mp_apply_mark_string_chars(mp, h, next_size);
3697 @ We also need to keep track of which characters are used in text nodes
3698 in the edge structure that is being shipped out. This is done by procedures
3699 that use the left-over |b3| field in the |char_info| words; i.e.,
3700 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3703 enum mp_char_mark_state {mp_unused=0, mp_used};
3706 void mp_mark_string_chars (MP mp,font_number f, char *s) ;
3709 void mp_mark_string_chars (MP mp,font_number f, char *s) {
3710 integer b; /* |char_base[f]| */
3711 ASCII_code bc,ec; /* only characters between these bounds are marked */
3712 char *k; /* an index into string |s| */
3718 if ( (*k>=bc)&&(*k<=ec) )
3719 mp->font_info[b+*k].qqqq.b3=mp_used;
3726 void mp_unmark_font (MP mp,font_number f) ;
3729 void mp_unmark_font (MP mp,font_number f) {
3730 int k; /* an index into |font_info| */
3731 for (k= mp->char_base[f]+mp->font_bc[f];
3732 k<=mp->char_base[f]+mp->font_ec[f];
3734 mp->font_info[k].qqqq.b3=mp_unused;
3739 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) ;
3742 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
3743 quarterword next_size; /* the size index for fonts being listed */
3744 pointer *cur_fsize; /* current positions in |font_sizes| */
3745 boolean done_fonts; /* have we finished listing the fonts in the header? */
3746 font_number f; /* a font number for loops */
3747 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3748 mp_list_used_resources(mp, prologues, procset);
3749 mp_list_supplied_resources(mp, prologues, procset);
3750 mp_list_needed_resources(mp, prologues);
3751 mp_ps_print_nl(mp, "%%EndComments");
3752 mp_ps_print_nl(mp, "%%BeginProlog");
3754 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
3756 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3757 mp_ps_print_nl(mp, "/bd{bind def}bind def"
3758 "/fshow {exch findfont exch scalefont setfont show}bd");
3759 if ( procset>0 ) @<Print the procset@>;
3760 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
3761 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3762 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3763 "/fmd{/FontMatrix exch def}bd");
3764 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3765 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3766 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3767 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3768 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3769 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3770 mp_ps_print_nl(mp, "%%EndResource");
3771 @<Include encodings and fonts for edge structure~|h|@>;
3772 mp_ps_print_nl(mp, "%%EndProlog");
3773 mp_ps_print_nl(mp, "%%BeginSetup");
3775 for (f=null_font+1;f<=mp->last_fnum;f++) {
3776 if ( mp_has_font_size(mp,f) ) {
3777 if ( mp_has_fm_entry(mp,f,NULL) ) {
3778 mp_write_font_definition(mp,f, prologues);
3779 mp_ps_name_out(mp, mp->font_name[f],true);
3780 mp_ps_print_defined_name(mp,f, prologues);
3781 mp_ps_print(mp, " def");
3784 mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3786 mp_ps_name_out(mp, mp->font_name[f],true);
3787 mp_ps_name_out(mp, mp->font_name[f],true);
3788 mp_ps_print(mp, " def");
3793 mp_ps_print_nl(mp, "%%EndSetup");
3794 mp_ps_print_nl(mp, "%%Page: 1 1");
3796 mp_xfree(cur_fsize);
3800 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues);
3805 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) {
3806 quarterword next_size; /* the size index for fonts being listed */
3807 pointer *cur_fsize; /* current positions in |font_sizes| */
3808 int ff; /* a loop counter */
3809 boolean done_fonts; /* have we finished listing the fonts in the header? */
3810 font_number f; /* a font number for loops */
3811 scaled ds; /* design size and scale factor for a text node */
3812 font_number ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3813 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3814 if ( prologues>0 ) {
3815 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3816 |font_sizes| and eliminate duplicates@>;
3819 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3820 do { done_fonts=true;
3821 for (f=null_font+1;f<=mp->last_fnum;f++) {
3822 if ( cur_fsize[f]!=null ) {
3823 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3825 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
3827 if ( ! done_fonts ) {
3828 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3831 } while (! done_fonts);
3833 mp_xfree(cur_fsize);
3837 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3838 for (f=null_font+1;f<= mp->last_fnum;f++)
3839 cur_fsize[f]=mp->font_sizes[f]
3841 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3842 so we carefully remove duplicates. There is no harm in using a slow, brute-force
3845 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3848 for (f=null_font+1;f<= mp->last_fnum;f++) {
3849 if ( mp->font_sizes[f]!=null ) {
3850 if ( ldf==null_font )
3851 mp_ps_print_nl(mp, "%%DocumentFonts:");
3852 for (ff=ldf;ff>=null_font;ff--) {
3853 if ( mp->font_sizes[ff]!=null )
3854 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3857 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3858 mp_ps_print_nl(mp, "%%+");
3859 mp_ps_print_char(mp, ' ');
3860 mp_ps_print(mp, mp->font_ps_name[f]);
3869 static void mp_hex_digit_out (MP mp,small_number d) {
3870 if ( d<10 ) mp_ps_print_char(mp, d+'0');
3871 else mp_ps_print_char(mp, d+'a'-10);
3874 @ We output the marks as a hexadecimal bit string starting at |c| or
3875 |font_bc[f]|, whichever is greater. If the output has to be truncated
3876 to avoid exceeding |emergency_line_length| the return value says where to
3877 start scanning next time.
3880 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3883 @d emergency_line_length 255
3884 /* \ps\ output lines can be this long in unusual circumstances */
3887 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3888 eight_bits bc,ec; /* only encode characters between these bounds */
3889 integer lim; /* the maximum number of marks to encode before truncating */
3890 int p; /* |font_info| index for the current character */
3891 int d,b; /* used to construct a hexadecimal digit */
3892 lim=4*(emergency_line_length-mp->ps->ps_offset-4);
3896 @<Restrict the range |bc..ec| so that it contains no unused characters
3897 at either end and has length at most |lim|@>;
3898 @<Print the initial label indicating that the bitmap starts at |bc|@>;
3899 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3900 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3906 @ We could save time by setting the return value before the loop that
3907 decrements |ec|, but there is no point in being so tricky.
3909 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3910 p=mp->char_base[f]+bc;
3911 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3914 if ( ec>=bc+lim ) ec=bc+lim-1;
3915 p=mp->char_base[f]+ec;
3916 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3920 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3921 mp_ps_print_char(mp, ' ');
3922 mp_hex_digit_out(mp, bc / 16);
3923 mp_hex_digit_out(mp, bc % 16);
3924 mp_ps_print_char(mp, ':')
3928 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3930 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3932 mp_hex_digit_out(mp, d);
3935 if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
3938 mp_hex_digit_out(mp, d)
3941 @ Here is a simple function that determines whether there are any marked
3942 characters in font~|f| with character code at least~|c|.
3945 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
3948 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
3949 int p; /* |font_info| index for the current character */
3950 for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
3951 if ( mp->font_info[p].qqqq.b3==mp_used )
3958 @ If the file name is so long that it can't be printed without exceeding
3959 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
3960 line. We might have to repeat line in order to get the character usage
3961 information to fit within |emergency_line_length|.
3963 TODO: these two defines are also defined in mp.w!
3965 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
3966 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
3968 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
3970 while ( mp_check_ps_marks(mp, f,t) ) {
3971 mp_ps_print_nl(mp, "%*Font: ");
3972 if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
3974 mp_ps_print(mp, mp->font_name[f]);
3975 mp_ps_print_char(mp, ' ');
3976 ds=(mp->font_dsize[f] + 8) / 16;
3977 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
3978 if ( mp->ps->ps_offset+12>emergency_line_length ) break;
3979 mp_ps_print_char(mp, ' ');
3980 mp_ps_print_scaled(mp, ds);
3981 if ( mp->ps->ps_offset+5>emergency_line_length ) break;
3982 t=mp_ps_marks_out(mp, f,t);
3984 cur_fsize[f]=link(cur_fsize[f]);
3987 @ @<Print the procset@>=
3989 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
3990 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
3991 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
3992 "/p{closepath}bd/n{newpath}bd");
3993 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
3994 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
3995 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
3996 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
3997 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4001 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4002 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
4003 might not have reasonable |font_ps_name| entries, but we just charge ahead
4004 anyway. The user should not make \&{prologues} positive if this will cause
4006 @:prologues_}{\&{prologues} primitive@>
4009 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset);
4012 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
4015 ldf = mp_print_font_comments (mp, h, prologues);
4017 if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
4018 mp_read_psname_table(mp);
4019 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4020 if ( (prologues>0)||(procset>0) ) {
4021 if ( ldf!=null_font ) {
4022 if ( prologues>0 ) {
4023 for (f=null_font+1;f<=mp->last_fnum;f++) {
4024 if ( mp_has_font_size(mp,f) ) {
4025 mp_ps_name_out(mp, mp->font_name[f],true);
4026 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4027 mp_ps_print(mp, " def");
4032 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4038 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4039 if ( (prologues>0)&&(ldf!=null_font) )
4041 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4043 mp_ps_print_nl(mp, "/bd{bind def}bind def");
4044 @<Print the procset@>;
4045 mp_ps_print_nl(mp, "%%EndResource");
4049 mp_ps_print(mp, "%%EndProlog");
4050 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4053 @ \MP\ used to have one single routine to print to both `write' files
4054 and the PostScript output. Web2c redefines ``Character |k| cannot be
4055 printed'', and that resulted in some bugs where 8-bit characters were
4056 written to the PostScript file (reported by Wlodek Bzyl).
4058 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4059 a plain space, since that makes it easier to parse the result file
4062 @<Character |k| is not allowed in PostScript output@>=
4065 @ We often need to print a pair of coordinates.
4068 void mp_ps_pair_out (MP mp,scaled x, scaled y) {
4070 mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
4071 mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
4075 void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4078 void mp_ps_print_cmd (MP mp, const char *l, const char *s) {
4079 if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4080 else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4084 void mp_ps_print_cmd (MP mp, const char *l, const char *s) ;
4087 void mp_ps_string_out (MP mp, const char *s) {
4088 ASCII_code k; /* bits to be converted to octal */
4089 mp_ps_print(mp, "(");
4091 if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4092 mp_ps_print_char(mp, '\\');
4095 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4096 mp_ps_print_char(mp, '\\');
4097 mp_ps_print_char(mp, '0'+(k / 64));
4098 mp_ps_print_char(mp, '0'+((k / 8) % 8));
4099 mp_ps_print_char(mp, '0'+(k % 8));
4101 if ( (k=='(')||(k==')')||(k=='\\') )
4102 mp_ps_print_char(mp, '\\');
4103 mp_ps_print_char(mp, k);
4106 mp_ps_print_char(mp, ')');
4110 void mp_ps_string_out (MP mp, const char *s) ;
4112 @ This is a define because the function does not use its |mp| argument.
4114 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4117 static boolean mp_do_is_ps_name (char *s) {
4118 ASCII_code k; /* the character being checked */
4120 if ( (k<=' ')||(k>'~') ) return false;
4121 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4122 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4128 void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4131 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4132 ps_room(strlen(s)+2);
4133 mp_ps_print_char(mp, ' ');
4134 if ( mp_is_ps_name(mp, s) ) {
4135 if ( lit ) mp_ps_print_char(mp, '/');
4138 mp_ps_string_out(mp, s);
4139 if ( ! lit ) mp_ps_print(mp, "cvx ");
4140 mp_ps_print(mp, "cvn");
4145 @ These special comments described in the {\sl PostScript Language Reference
4146 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4147 We can't normally output ``conforming'' \ps\ because
4148 the structuring conventions don't allow us to say ``Please make sure the
4149 following characters are downloaded and define the \.{fshow} macro to access
4152 The exact bounding box is written out if |mp_prologues<0|, although this
4153 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4154 accurately. (Overfull boxes are avoided if an illustration is made to
4155 match a given \.{\char`\\hsize}.)
4158 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues);
4161 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) {
4163 mp_ps_print(mp, "%!PS");
4165 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
4166 mp_ps_print_nl(mp, "%%BoundingBox: ");
4167 if ( hh->_minx>hh->_maxx) {
4168 mp_ps_print(mp, "0 0 0 0");
4169 } else if ( prologues<0 ) {
4170 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4171 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4173 mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->_minx),mp_floor_scaled(mp, hh->_miny));
4174 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->_maxx),-mp_floor_scaled(mp, -hh->_maxy));
4176 mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
4177 if ( hh->_minx>hh->_maxx ) {
4178 mp_ps_print(mp, "0 0 0 0");
4180 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4181 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4183 mp_ps_print_nl(mp, "%%Creator: MetaPost ");
4184 mp_ps_print(mp, mp_metapost_version());
4185 mp_ps_print_nl(mp, "%%CreationDate: ");
4186 mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
4187 mp_ps_print_char(mp, '.');
4188 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month]));
4189 mp_ps_print_char(mp, '.');
4190 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
4191 mp_ps_print_char(mp, ':');
4192 t=mp_round_unscaled(mp, mp->internal[mp_time]);
4193 mp_ps_print_dd(mp, t / 60);
4194 mp_ps_print_dd(mp, t % 60);
4195 mp_ps_print_nl(mp, "%%Pages: 1");
4198 @ The most important output procedure is the one that gives the \ps\ version of
4202 typedef struct mp_knot {
4203 unsigned short left_type_field;
4204 unsigned short right_type_field;
4205 signed int x_coord_field;
4206 signed int y_coord_field;
4207 signed int left_x_field;
4208 signed int left_y_field;
4209 signed int right_x_field;
4210 signed int right_y_field;
4211 struct mp_knot * next_field;
4212 unsigned char originator_field;
4216 #define gr_left_type(A) (A)->left_type_field
4217 #define gr_right_type(A) (A)->right_type_field
4218 #define gr_x_coord(A) (A)->x_coord_field
4219 #define gr_y_coord(A) (A)->y_coord_field
4220 #define gr_left_x(A) (A)->left_x_field
4221 #define gr_left_y(A) (A)->left_y_field
4222 #define gr_right_x(A) (A)->right_x_field
4223 #define gr_right_y(A) (A)->right_y_field
4224 #define gr_next_knot(A) (A)->next_field
4225 #define gr_originator(A) (A)->originator_field
4228 mp_knot * mp_gr_insert_knot (MP mp, mp_knot *q, scaled x, scaled y) {
4229 /* returns the inserted knot */
4230 mp_knot *r; /* the new knot */
4231 r= mp_xmalloc(mp, 1, sizeof (mp_knot));
4232 gr_next_knot(r)=gr_next_knot(q); gr_next_knot(q)=r;
4233 gr_right_x(r)=gr_right_x(q);
4234 gr_right_y(r)=gr_right_y(q);
4237 gr_right_x(q)=gr_x_coord(q);
4238 gr_right_y(q)=gr_y_coord(q);
4239 gr_left_x(r)=gr_x_coord(r);
4240 gr_left_y(r)=gr_y_coord(r);
4241 gr_left_type(r)=mp_explicit;
4242 gr_right_type(r)=mp_explicit;
4243 gr_originator(r)=mp_program_code;
4248 @ If we want to duplicate a knot node, we can say |copy_knot|:
4251 mp_knot *mp_gr_copy_knot (MP mp, mp_knot *p) {
4252 mp_knot *q; /* the copy */
4253 q = mp_xmalloc(mp, 1, sizeof (mp_knot));
4254 memcpy(q,p,sizeof (mp_knot));
4255 gr_next_knot(q)=NULL;
4259 @ The |copy_path| routine makes a clone of a given path.
4262 mp_knot *mp_gr_copy_path (MP mp, mp_knot *p) {
4263 mp_knot *q, *pp, *qq; /* for list manipulation */
4266 q=mp_gr_copy_knot(mp, p);
4270 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4271 qq=gr_next_knot(qq);
4272 pp=gr_next_knot(pp);
4278 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4279 returns a pointer to the first node of the copy, if the path is a cycle,
4280 but to the final node of a non-cyclic copy. The global
4281 variable |path_tail| will point to the final node of the original path;
4282 this trick makes it easier to implement `\&{doublepath}'.
4284 All node types are assumed to be |endpoint| or |explicit| only.
4286 This function is currenly unused.
4289 mp_knot * mp_gr_htap_ypoc (MP mp, mp_knot *p) {
4290 mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4291 q=mp_xmalloc(mp, 1, sizeof (mp_knot)); /* this will correspond to |p| */
4294 gr_right_type(qq)=gr_left_type(pp);
4295 gr_left_type(qq)=gr_right_type(pp);
4296 gr_x_coord(qq)=gr_x_coord(pp);
4297 gr_y_coord(qq)=gr_y_coord(pp);
4298 gr_right_x(qq)=gr_left_x(pp);
4299 gr_right_y(qq)=gr_left_y(pp);
4300 gr_left_x(qq)=gr_right_x(pp);
4301 gr_left_y(qq)=gr_right_y(pp);
4302 gr_originator(qq)=gr_originator(pp);
4303 if ( gr_next_knot(pp)==p ) {
4305 /* |mp->path_tail=pp;| */ /* ? */
4308 rr=mp_xmalloc(mp, 1, sizeof (mp_knot));
4309 gr_next_knot(rr)=qq;
4311 pp=gr_next_knot(pp);
4315 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4316 calling the following subroutine.
4319 void mp_do_gr_toss_knot_list (mp_knot *p) ;
4322 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4325 void mp_do_gr_toss_knot_list (mp_knot * p) {
4326 mp_knot *q; /* the node being freed */
4327 mp_knot *r; /* the next node */
4340 void mp_gr_ps_path_out (MP mp, mp_knot *h) {
4341 mp_knot *p, *q; /* for scanning the path */
4342 scaled d; /* a temporary value */
4343 boolean curved; /* |true| unless the cubic is almost straight */
4345 mp_ps_print_cmd(mp, "newpath ","n ");
4346 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4347 mp_ps_print_cmd(mp, "moveto","m");
4350 if ( gr_right_type(p)==mp_endpoint ) {
4351 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4355 @<Start a new line and print the \ps\ commands for the curve from
4359 mp_ps_print_cmd(mp, " closepath"," p");
4362 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4364 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4367 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4368 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4369 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4370 mp_ps_print_cmd(mp, "curveto","c");
4372 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4373 mp_ps_print_cmd(mp, "lineto","l");
4376 @ Two types of straight lines come up often in \MP\ paths:
4377 cubics with zero initial and final velocity as created by |make_path| or
4378 |make_envelope|, and cubics with control points uniformly spaced on a line
4379 as created by |make_choices|.
4381 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4383 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4384 if ( gr_right_x(p)==gr_x_coord(p) )
4385 if ( gr_right_y(p)==gr_y_coord(p) )
4386 if ( gr_left_x(q)==gr_x_coord(q) )
4387 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4388 d=gr_left_x(q)-gr_right_x(p);
4389 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4390 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4391 { d=gr_left_y(q)-gr_right_y(p);
4392 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4393 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4396 @ The colored objects use a struct with anonymous fields to express the color parts:
4400 int _a_val, _b_val, _c_val, _d_val;
4403 @ The exported form of a dash pattern is simpler than the internal
4404 format, it is closely modelled to the PostScript model. The array of
4405 dashes is ended by a single negative value, because this is not
4406 allowed in PostScript.
4416 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
4419 void mp_do_gr_toss_dashes(mp_dash_object *dl);
4422 void mp_do_gr_toss_dashes(mp_dash_object *dl) {
4425 mp_xfree(dl->array_field);
4431 mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) {
4432 mp_dash_object *q = NULL;
4436 q = mp_xmalloc(mp, 1, sizeof (mp_dash_object));
4437 memcpy (q,dl,sizeof(mp_dash_object));
4438 if (dl->array_field != NULL) {
4440 while (*(dl->array_field+i) != -1) i++;
4441 q->array_field = mp_xmalloc(mp, i, sizeof (scaled));
4442 memcpy(q->array_field,dl->array_field, (i*sizeof(scaled)));
4448 @ Now for outputting the actual graphic objects. First, set up some
4449 structures and access macros.
4451 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4454 #define gr_type(A) (A)->_type_field
4455 #define gr_link(A) (A)->_link_field
4456 #define gr_color_model(A) (A)->color_model_field
4457 #define gr_red_val(A) (A)->color_field._a_val
4458 #define gr_green_val(A) (A)->color_field._b_val
4459 #define gr_blue_val(A) (A)->color_field._c_val
4460 #define gr_cyan_val(A) (A)->color_field._a_val
4461 #define gr_magenta_val(A) (A)->color_field._b_val
4462 #define gr_yellow_val(A) (A)->color_field._c_val
4463 #define gr_black_val(A) (A)->color_field._d_val
4464 #define gr_grey_val(A) (A)->color_field._a_val
4465 #define gr_path_p(A) (A)->path_p_field
4466 #define gr_htap_p(A) ((mp_fill_object *)A)->htap_p_field
4467 #define gr_pen_p(A) (A)->pen_p_field
4468 #define gr_ljoin_val(A) (A)->ljoin_field
4469 #define gr_lcap_val(A) ((mp_stroked_object *)A)->lcap_field
4470 #define gr_miterlim_val(A) (A)->miterlim_field
4471 #define gr_pre_script(A) (A)->pre_script_field
4472 #define gr_post_script(A) (A)->post_script_field
4473 #define gr_dash_p(A) ((mp_stroked_object *)A)->dash_p_field
4474 #define gr_name_type(A) ((mp_text_object *)A)->name_type_field
4475 #define gr_text_p(A) ((mp_text_object *)A)->text_p_field
4476 #define gr_font_n(A) ((mp_text_object *)A)->font_n_field
4477 #define gr_font_name(A) ((mp_text_object *)A)->font_name_field
4478 #define gr_font_dsize(A) ((mp_text_object *)A)->font_dsize_field
4479 #define gr_width_val(A) ((mp_text_object *)A)->width_field
4480 #define gr_height_val(A) ((mp_text_object *)A)->height_field
4481 #define gr_depth_val(A) ((mp_text_object *)A)->depth_field
4482 #define gr_tx_val(A) ((mp_text_object *)A)->tx_field
4483 #define gr_ty_val(A) ((mp_text_object *)A)->ty_field
4484 #define gr_txx_val(A) ((mp_text_object *)A)->txx_field
4485 #define gr_txy_val(A) ((mp_text_object *)A)->txy_field
4486 #define gr_tyx_val(A) ((mp_text_object *)A)->tyx_field
4487 #define gr_tyy_val(A) ((mp_text_object *)A)->tyy_field
4490 #define GRAPHIC_BODY \
4492 struct mp_graphic_object * _link_field
4494 typedef struct mp_graphic_object {
4496 } mp_graphic_object;
4498 typedef struct mp_text_object {
4500 char *pre_script_field;
4501 char *post_script_field;
4502 mp_color color_field;
4503 unsigned char color_model_field;
4504 unsigned char name_type_field;
4506 char *font_name_field ;
4507 unsigned int font_dsize_field ;
4508 unsigned int font_n_field ;
4520 typedef struct mp_fill_object {
4522 char *pre_script_field;
4523 char *post_script_field;
4524 mp_color color_field;
4525 unsigned char color_model_field;
4526 unsigned char ljoin_field ;
4527 mp_knot * path_p_field;
4528 mp_knot * htap_p_field;
4529 mp_knot * pen_p_field;
4530 int miterlim_field ;
4533 typedef struct mp_stroked_object {
4535 char *pre_script_field;
4536 char *post_script_field;
4537 mp_color color_field;
4538 unsigned char color_model_field;
4539 unsigned char ljoin_field ;
4540 unsigned char lcap_field ;
4541 mp_knot * path_p_field;
4542 mp_knot * pen_p_field;
4543 int miterlim_field ;
4544 mp_dash_object *dash_p_field;
4545 } mp_stroked_object;
4547 typedef struct mp_clip_object {
4549 mp_knot * path_p_field;
4552 typedef struct mp_bounds_object {
4554 mp_knot * path_p_field;
4557 typedef struct mp_special_object {
4559 char *pre_script_field;
4560 } mp_special_object ;
4562 typedef struct mp_edge_object {
4563 struct mp_graphic_object * body;
4564 struct mp_edge_object * _next;
4567 int _minx, _miny, _maxx, _maxy;
4570 @ @<Exported function headers@>=
4571 mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4574 mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4575 mp_graphic_object *p;
4578 case mp_fill_code: size = sizeof(mp_fill_object); break;
4579 case mp_stroked_code: size = sizeof(mp_stroked_object); break;
4580 case mp_text_code: size = sizeof(mp_text_object); break;
4581 case mp_start_clip_code: size = sizeof(mp_clip_object); break;
4582 case mp_start_bounds_code: size = sizeof(mp_bounds_object); break;
4583 case mp_special_code: size = sizeof(mp_special_object); break;
4584 default: size = sizeof(mp_graphic_object); break;
4586 p = (mp_graphic_object *)mp_xmalloc(mp,1,size);
4592 @ We need to keep track of several parameters from the \ps\ graphics state.
4594 This allows us to be sure that \ps\ has the correct values when they are
4595 needed without wasting time and space setting them unnecessarily.
4597 @d gs_red mp->ps->gs_state->red_field
4598 @d gs_green mp->ps->gs_state->green_field
4599 @d gs_blue mp->ps->gs_state->blue_field
4600 @d gs_black mp->ps->gs_state->black_field
4601 @d gs_colormodel mp->ps->gs_state->colormodel_field
4602 @d gs_ljoin mp->ps->gs_state->ljoin_field
4603 @d gs_lcap mp->ps->gs_state->lcap_field
4604 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4605 @d gs_miterlim mp->ps->gs_state->miterlim_field
4606 @d gs_dash_p mp->ps->gs_state->dash_p_field
4607 @d gs_dash_init_done mp->ps->gs_state->dash_done_field
4608 @d gs_previous mp->ps->gs_state->previous_field
4609 @d gs_width mp->ps->gs_state->width_field
4612 typedef struct _gs_state {
4614 scaled green_field ;
4616 scaled black_field ;
4617 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4618 quarterword colormodel_field ;
4619 /* the current colormodel */
4620 quarterword ljoin_field ;
4621 quarterword lcap_field ;
4622 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4623 quarterword adj_wx_field ;
4624 /* what resolution-dependent adjustment applies to the width */
4625 scaled miterlim_field ;
4626 /* the value from the last \&{setmiterlimit} command */
4627 mp_dash_object * dash_p_field ;
4628 /* edge structure for last \&{setdash} command */
4629 boolean dash_done_field ; /* to test for initial \&{setdash} */
4630 struct _gs_state * previous_field ;
4631 /* backlink to the previous |_gs_state| structure */
4632 scaled width_field ;
4633 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4638 struct _gs_state * gs_state;
4641 mp->ps->gs_state=NULL;
4643 @ @<Dealloc variables@>=
4644 mp_xfree(mp->ps->gs_state);
4646 @ To avoid making undue assumptions about the initial graphics state, these
4647 parameters are given special values that are guaranteed not to match anything
4648 in the edge structure being shipped out. On the other hand, the initial color
4649 should be black so that the translation of an all-black picture will have no
4650 \&{setcolor} commands. (These would be undesirable in a font application.)
4651 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4652 to recover from a situation where we have lost track of the graphics state.
4655 void mp_gs_unknown_graphics_state (MP mp,scaled c) ;
4658 @d mp_void (null+1) /* a null pointer different from |null| */
4660 @c void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4661 struct _gs_state *p; /* to shift graphic states around */
4662 if ( (c==0)||(c==-1) ) {
4663 if ( mp->ps->gs_state==NULL ) {
4664 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4667 while ( gs_previous!=NULL ) {
4669 mp_xfree(mp->ps->gs_state);
4673 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4674 gs_colormodel=mp_uninitialized_model;
4679 gs_dash_init_done=false;
4681 } else if ( c==1 ) {
4682 p= mp->ps->gs_state;
4683 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4684 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4686 } else if ( c==2 ) {
4688 mp_xfree(mp->ps->gs_state);
4694 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4695 that \ps's idea of the graphics state agrees with what is stored in the object.
4698 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ;
4701 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) {
4702 /* get ready to output graphical object |p| */
4703 mp_knot *pp, *path_p; /* for list manipulation */
4705 scaled wx,wy,ww; /* dimensions of pen bounding box */
4706 boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4707 integer tx,ty; /* temporaries for computing |adj_wx| */
4708 if ( gr_has_color(p) )
4709 @<Make sure \ps\ will use the right color for object~|p|@>;
4710 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) {
4711 if (gr_type(p)==mp_fill_code) {
4712 pp = gr_pen_p((mp_fill_object *)p);
4713 path_p = gr_path_p((mp_fill_object *)p);
4715 pp = gr_pen_p((mp_stroked_object *)p);
4716 path_p = gr_path_p((mp_stroked_object *)p);
4719 if ( pen_is_elliptical(pp) ) {
4720 @<Generate \ps\ code that sets the stroke width to the
4721 appropriate rounded value@>;
4722 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4723 @<Decide whether the line cap parameter matters and set it if necessary@>;
4724 @<Set the other numeric parameters as needed for object~|p|@>;
4727 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
4730 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4731 if ( gr_type(p)==mp_stroked_code ) {
4732 mp_stroked_object *ts = (mp_stroked_object *)p;
4733 if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) )
4734 if ( gs_lcap!=gr_lcap_val(ts) ) {
4736 mp_ps_print_char(mp, ' ');
4737 mp_ps_print_char(mp, '0'+gr_lcap_val(ts));
4738 mp_ps_print_cmd(mp, " setlinecap"," lc");
4739 gs_lcap=gr_lcap_val(ts);
4744 @d set_ljoin_miterlim(p)
4745 if ( gs_ljoin!=gr_ljoin_val(p) ) {
4747 mp_ps_print_char(mp, ' ');
4748 mp_ps_print_char(mp, '0'+gr_ljoin_val(p));
4749 mp_ps_print_cmd(mp, " setlinejoin"," lj");
4750 gs_ljoin=gr_ljoin_val(p);
4752 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4754 mp_ps_print_char(mp, ' ');
4755 mp_ps_print_scaled(mp, gr_miterlim_val(p));
4756 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4757 gs_miterlim=gr_miterlim_val(p);
4760 @<Set the other numeric parameters as needed for object~|p|@>=
4761 if ( gr_type(p)==mp_stroked_code ) {
4762 mp_stroked_object *ts = (mp_stroked_object *)p;
4763 set_ljoin_miterlim(ts);
4765 mp_fill_object *ts = (mp_fill_object *)p;
4766 set_ljoin_miterlim(ts);
4770 @d set_color_objects(pq)
4771 object_color_model = pq->color_model_field;
4772 object_color_a = pq->color_field._a_val;
4773 object_color_b = pq->color_field._b_val;
4774 object_color_c = pq->color_field._c_val;
4775 object_color_d = pq->color_field._d_val;
4777 @<Make sure \ps\ will use the right color for object~|p|@>=
4779 int object_color_model;
4780 int object_color_a, object_color_b, object_color_c, object_color_d ;
4781 if (gr_type(p) == mp_fill_code) {
4782 mp_fill_object *pq = (mp_fill_object *)p;
4783 set_color_objects(pq);
4784 } else if (gr_type(p) == mp_stroked_code) {
4785 mp_stroked_object *pq = (mp_stroked_object *)p;
4786 set_color_objects(pq);
4788 mp_text_object *pq = (mp_text_object *)p;
4789 set_color_objects(pq);
4792 if ( object_color_model==mp_rgb_model) {
4793 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)||
4794 (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) {
4795 gs_red = object_color_a;
4796 gs_green = object_color_b;
4797 gs_blue = object_color_c;
4799 gs_colormodel=mp_rgb_model;
4801 mp_ps_print_char(mp, ' ');
4802 mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
4803 mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
4804 mp_ps_print_scaled(mp, gs_blue);
4805 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4808 } else if ( object_color_model==mp_cmyk_model) {
4809 if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)||
4810 (gs_blue!=object_color_c)||(gs_black!=object_color_d)||
4811 (gs_colormodel!=mp_cmyk_model) ) {
4812 gs_red = object_color_a;
4813 gs_green = object_color_b;
4814 gs_blue = object_color_c;
4815 gs_black = object_color_d;
4816 gs_colormodel=mp_cmyk_model;
4818 mp_ps_print_char(mp, ' ');
4819 mp_ps_print_scaled(mp, gs_red);
4820 mp_ps_print_char(mp, ' ');
4821 mp_ps_print_scaled(mp, gs_green);
4822 mp_ps_print_char(mp, ' ');
4823 mp_ps_print_scaled(mp, gs_blue);
4824 mp_ps_print_char(mp, ' ');
4825 mp_ps_print_scaled(mp, gs_black);
4826 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4829 } else if ( object_color_model==mp_grey_model ) {
4830 if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) {
4831 gs_red = object_color_a;
4835 gs_colormodel=mp_grey_model;
4837 mp_ps_print_char(mp, ' ');
4838 mp_ps_print_scaled(mp, gs_red);
4839 mp_ps_print_cmd(mp, " setgray"," G");
4842 } else if ( object_color_model==mp_no_model ) {
4843 gs_colormodel=mp_no_model;
4847 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4848 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4849 @:setwidth}{\&{setwidth}command@>
4850 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4851 generate \ps\ code that computes the rounded value. For non-circular pens, the
4852 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4853 have the computed width.
4855 Rounding the width to whole pixels is not likely to improve the appearance of
4856 diagonal or curved strokes, but we do it anyway for consistency. The
4857 \&{truncate} command generated here tends to make all the strokes a little
4858 @:truncate}{\&{truncate} command@>
4859 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4860 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4861 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4864 @<Generate \ps\ code that sets the stroke width...@>=
4865 @<Set |wx| and |wy| to the width and height of the bounding box for
4867 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4868 important and set |adj_wx| and |ww| accordingly@>;
4869 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4872 mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
4874 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4876 if ( mp->internal[mp_procset]>0 ) {
4878 mp_ps_print_char(mp, ' ');
4879 mp_ps_print_scaled(mp, ww);
4880 mp_ps_print(mp, " vlw");
4883 mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
4884 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4891 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4892 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4893 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4894 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4896 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4897 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4900 @ The path is considered ``essentially horizontal'' if its range of
4901 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4902 vertical'' paths are detected similarly. This code ensures that no component
4903 of the pen transformation is more that |aspect_bound*(ww+1)|.
4905 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4906 more than this factor */
4911 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4913 if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound;
4914 else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound;
4915 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
4916 else { ww=wx; adj_wx=true; }
4918 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4919 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4920 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4921 bounding-box computation.
4924 boolean mp_gr_coord_rangeOK (mp_knot *h,
4925 small_number zoff, scaled dz);
4928 boolean mp_gr_coord_rangeOK (mp_knot *h,
4929 small_number zoff, scaled dz) {
4930 mp_knot *p; /* for scanning the path form |h| */
4931 scaled zlo,zhi; /* coordinate range so far */
4932 scaled z; /* coordinate currently being tested */
4933 if (zoff==do_x_loc) {
4937 while ( gr_right_type(p)!=mp_endpoint ) {
4939 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4940 p=gr_next_knot(p); z=gr_left_x(p);
4941 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4943 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4950 while ( gr_right_type(p)!=mp_endpoint ) {
4952 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4953 p=gr_next_knot(p); z=gr_left_y(p);
4954 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4956 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4963 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4965 else if ( z>zhi ) zhi=z;
4966 if ( zhi-zlo>dz ) return false
4968 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4969 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4970 @:stroke}{\&{stroke} command@>
4971 @:fill}{\&{fill} command@>
4972 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4975 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4976 if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) {
4982 if ( gs_dash_p!=NULL || gs_dash_init_done == false) {
4983 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4985 gs_dash_init_done=true;
4987 } else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) {
4988 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
4992 scaled mp_gr_get_pen_scale (MP mp, mp_knot *p) ;
4996 scaled mp_gr_get_pen_scale (MP mp, mp_knot *p) {
4997 return mp_sqrt_det(mp,
4998 gr_left_x(p)-gr_x_coord(p), gr_right_x(p)-gr_x_coord(p),
4999 gr_left_y(p)-gr_y_coord(p), gr_right_y(p)-gr_y_coord(p));
5003 @ The original code had a check here to ensure that the result from
5004 |mp_take_scaled| did not go out of bounds.
5006 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5008 if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array_field==NULL)) {
5009 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5013 mp_ps_print(mp, " [");
5014 for (i=0; *(hh->array_field+i) != -1;i++) {
5016 mp_ps_print_scaled(mp, *(hh->array_field+i));
5017 mp_ps_print_char(mp, ' ') ;
5020 mp_ps_print(mp, "] ");
5021 mp_ps_print_scaled(mp, hh->offset_field);
5022 mp_ps_print_cmd(mp, " setdash"," sd");
5027 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ;
5029 @ This function test if |h| and |hh| represent the same dash pattern.
5032 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) {
5035 if ( h==hh ) ret=true;
5036 else if ( (h==NULL)||(hh==NULL) ) ret=false;
5037 else if ( h->offset_field!=hh->offset_field ) ret=false;
5038 else if ( h->array_field == hh->array_field) ret=true;
5039 else if ( h->array_field == NULL || hh->array_field == NULL) ret=false;
5040 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5044 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5046 while (*(h->array_field+i)!=-1 &&
5047 *(hh->array_field+i)!=-1 &&
5048 *(h->array_field+i) == *(hh->array_field+i)) i++;
5050 if (*(h->array_field+(i))==-1 && *(hh->array_field+(i)) == -1)
5055 @ When stroking a path with an elliptical pen, it is necessary to transform
5056 the coordinate system so that a unit circular pen will have the desired shape.
5057 To keep this transformation local, we enclose it in a
5058 $$\&{gsave}\ldots\&{grestore}$$
5059 block. Any translation component must be applied to the path being stroked
5060 while the rest of the transformation must apply only to the pen.
5061 If |fill_also=true|, the path is to be filled as well as stroked so we must
5062 insert commands to do this after giving the path.
5065 void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) ;
5068 @c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) {
5069 /* generate an elliptical pen stroke from object |h| */
5070 scaled txx,txy,tyx,tyy; /* transformation parameters */
5071 mp_knot *p; /* the pen to stroke with */
5072 scaled d1,det; /* for tweaking transformation parameters */
5073 integer s; /* also for tweaking transformation paramters */
5074 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5076 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5078 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5079 if (gr_type(h)==mp_fill_code) {
5080 mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h));
5082 mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h));
5084 if ( mp->internal[mp_procset]==0 ) {
5085 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5086 @<Issue \ps\ commands to transform the coordinate system@>;
5087 mp_ps_print(mp, " stroke");
5088 if ( transformed ) mp_ps_print(mp, " grestore");
5090 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5091 if ( (txy!=0)||(tyx!=0) ) {
5092 mp_ps_print(mp, " [");
5093 mp_ps_pair_out(mp, txx,tyx);
5094 mp_ps_pair_out(mp, txy,tyy);
5095 mp_ps_print(mp, "0 0] t");
5096 } else if ((txx!=unity)||(tyy!=unity) ) {
5097 mp_ps_pair_out(mp,txx,tyy);
5098 mp_ps_print(mp, " s");
5100 mp_ps_print(mp, " S");
5101 if ( transformed ) mp_ps_print(mp, " Q");
5106 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5107 if (gr_type(h)==mp_fill_code) {
5108 p=gr_pen_p((mp_fill_object *)h);
5110 p=gr_pen_p((mp_stroked_object *)h);
5116 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5117 mp_ps_print_nl(mp, "");
5118 mp_ps_print_cmd(mp, "gsave ","q ");
5119 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5120 mp_ps_print(mp, "translate ");
5127 mp_ps_print_nl(mp, "");
5129 @<Adjust the transformation to account for |gs_width| and output the
5130 initial \&{gsave} if |transformed| should be |true|@>
5132 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5133 if ( gs_width!=unity ) {
5134 if ( gs_width==0 ) {
5135 txx=unity; tyy=unity;
5137 txx=mp_make_scaled(mp, txx,gs_width);
5138 txy=mp_make_scaled(mp, txy,gs_width);
5139 tyx=mp_make_scaled(mp, tyx,gs_width);
5140 tyy=mp_make_scaled(mp, tyy,gs_width);
5143 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5144 if ( (! transformed) ){
5145 mp_ps_print_cmd(mp, "gsave ","q ");
5150 @ @<Issue \ps\ commands to transform the coordinate system@>=
5151 if ( (txy!=0)||(tyx!=0) ){
5153 mp_ps_print_char(mp, '[');
5154 mp_ps_pair_out(mp, txx,tyx);
5155 mp_ps_pair_out(mp, txy,tyy);
5156 mp_ps_print(mp, "0 0] concat");
5157 } else if ( (txx!=unity)||(tyy!=unity) ){
5159 mp_ps_pair_out(mp, txx,tyy);
5160 mp_ps_print(mp, "scale");
5163 @ The \ps\ interpreter will probably abort if it encounters a singular
5164 transformation matrix. The determinant must be large enough to ensure that
5165 the printed representation will be nonsingular. Since the printed
5166 representation is always within $2^{-17}$ of the internal |scaled| value, the
5167 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5168 the magnitudes of |txx/65536|, |txy/65536|, etc.
5170 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5171 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5173 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5174 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5175 d1=4*aspect_bound+1;
5176 if ( abs(det)<d1 ) {
5177 if ( det>=0 ) { d1=d1-det; s=1; }
5178 else { d1=-d1-det; s=-1; };
5180 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5181 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5182 else txx=txx+(d1+s*abs(tyy)) / tyy;
5184 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5185 else txy=txy+(d1+s*abs(tyx)) / tyx;
5189 @ Here is a simple routine that just fills a cycle.
5192 void mp_gr_ps_fill_out (MP mp, mp_knot *p);
5195 void mp_gr_ps_fill_out (MP mp, mp_knot *p) { /* fill cyclic path~|p| */
5196 mp_gr_ps_path_out(mp, p);
5197 mp_ps_print_cmd(mp, " fill"," F");
5201 @ A text node may specify an arbitrary transformation but the usual case
5202 involves only shifting, scaling, and occasionally rotation. The purpose
5203 of |choose_scale| is to select a scale factor so that the remaining
5204 transformation is as ``nice'' as possible. The definition of ``nice''
5205 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5206 nice because they work out well for bitmap fonts. The code here selects
5207 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5208 non-shifting part of the transformation matrix. It is careful to avoid
5209 additions that might cause undetected overflow.
5212 scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) ;
5214 @ @c scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) {
5215 /* |p| should point to a text node */
5216 scaled a,b,c,d,ad,bc; /* temporary values */
5221 if ( a<0 ) negate(a);
5222 if ( b<0 ) negate(b);
5223 if ( c<0 ) negate(c);
5224 if ( d<0 ) negate(d);
5227 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5230 @ The potential overflow here is caused by the fact the returned value
5231 has to fit in a |name_type|, which is a quarterword.
5233 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
5236 quarterword mp_size_index (MP mp, font_number f, scaled s) ;
5239 quarterword mp_size_index (MP mp, font_number f, scaled s) {
5240 pointer p,q; /* the previous and current font size nodes */
5241 quarterword i; /* the size index for |q| */
5242 q=mp->font_sizes[f];
5245 if ( abs(s-sc_factor(q))<=fscale_tolerance )
5248 { p=q; q=link(q); incr(i); };
5249 if ( i==max_quarterword )
5250 mp_overflow(mp, "sizes per font",max_quarterword);
5251 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
5253 q=mp_get_node(mp, font_size_size);
5255 if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
5260 scaled mp_indexed_size (MP mp,font_number f, quarterword j);
5263 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
5264 pointer p; /* a font size node */
5265 quarterword i; /* the size index for |p| */
5266 p=mp->font_sizes[f];
5268 if ( p==null ) mp_confusion(mp, "size");
5271 if ( p==null ) mp_confusion(mp, "size");
5273 return sc_factor(p);
5277 void mp_clear_sizes (MP mp) ;
5279 @ @c void mp_clear_sizes (MP mp) {
5280 font_number f; /* the font whose size list is being cleared */
5281 pointer p; /* current font size nodes */
5282 for (f=null_font+1;f<=mp->last_fnum;f++) {
5283 while ( mp->font_sizes[f]!=null ) {
5284 p=mp->font_sizes[f];
5285 mp->font_sizes[f]=link(p);
5286 mp_free_node(mp, p,font_size_size);
5291 @ A text node may specify an arbitrary transformation but the usual case
5292 involves only shifting, scaling, and occasionally rotation. The purpose
5293 of |choose_scale| is to select a scale factor so that the remaining
5294 transformation is as ``nice'' as possible. The definition of ``nice''
5295 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5296 nice because they work out well for bitmap fonts. The code here selects
5297 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5298 non-shifting part of the transformation matrix. It is careful to avoid
5299 additions that might cause undetected overflow.
5301 @<Declare the \ps\ output procedures@>=
5302 scaled mp_choose_scale (MP mp, mp_graphic_object *p) ;
5304 @ @c scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
5305 /* |p| should point to a text node */
5306 scaled a,b,c,d,ad,bc; /* temporary values */
5311 if ( (a<0) ) negate(a);
5312 if ( (b<0) ) negate(b);
5313 if ( (c<0) ) negate(c);
5314 if ( (d<0) ) negate(d);
5317 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5320 @ There may be many sizes of one font and we need to keep track of the
5321 characters used for each size. This is done by keeping a linked list of
5322 sizes for each font with a counter in each text node giving the appropriate
5323 position in the size list for its font.
5325 @d font_size_size 2 /* size of a font size node */
5329 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size);
5332 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) {
5333 mp_graphic_object * p;
5335 while ( p!= NULL ) {
5336 if ( gr_type(p)==mp_text_code ) {
5337 if ( gr_font_n(p)!=null_font ) {
5338 if ( gr_name_type(p)==next_size )
5339 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p));
5346 @ @<Unmark all marked characters@>=
5347 for (f=null_font+1;f<=mp->last_fnum;f++) {
5348 if ( mp->font_sizes[f]!=null ) {
5349 mp_unmark_font(mp, f);
5350 mp->font_sizes[f]=null;
5354 @ @<Scan all the text nodes and mark the used ...@>=
5357 if ( gr_type(p)==mp_text_code ) {
5359 if (f!=null_font ) {
5360 switch (prologues) {
5363 mp->font_sizes[f] = mp_void;
5364 mp_mark_string_chars(mp, f, gr_text_p(p));
5365 if (mp_has_fm_entry(mp,f,NULL) ) {
5366 if (mp->font_enc_name[f]==NULL )
5367 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
5368 mp->font_ps_name[f] = mp_fm_font_name(mp,f);
5372 mp->font_sizes[f]=mp_void;
5375 gr_name_type(p)=mp_size_index(mp, f,mp_choose_scale(mp, p));
5376 if ( gr_name_type(p)==0 )
5377 mp_mark_string_chars(mp, f, gr_text_p(p));
5386 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5388 @<Exported function ...@>=
5389 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) ;
5392 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) {
5393 mp_graphic_object *p;
5394 scaled ds,scf; /* design size and scale factor for a text node */
5395 font_number f; /* for loops over fonts while (un)marking characters */
5396 boolean transformed; /* is the coordinate system being transformed? */
5397 MP mp = hh->_parent;
5400 mp->jump_buf = &buf;
5401 if (setjmp(*(mp->jump_buf)))
5404 if (mp->history >= mp_fatal_error_stop ) return 1;
5406 prologues = (mp->internal[mp_prologues]>>16);
5408 procset = (mp->internal[mp_procset]>>16);
5409 mp_open_output_file(mp);
5410 mp_print_initial_comment(mp, hh, prologues);
5412 @<Unmark all marked characters@>;
5413 if ( prologues==2 || prologues==3 ) {
5414 mp_reload_encodings(mp);
5416 @<Scan all the text nodes and mark the used characters@>;
5417 if ( prologues==2 || prologues==3 ) {
5418 mp_print_improved_prologue(mp, hh, prologues, procset);
5420 mp_print_prologue(mp, hh, prologues, procset);
5422 mp_gs_unknown_graphics_state(mp, 0);
5425 if ( gr_has_color(p) ) {
5426 @<Write |pre_script| of |p|@>;
5428 mp_gr_fix_graphics_state(mp, p);
5429 switch (gr_type(p)) {
5431 if ( gr_pen_p((mp_fill_object *)p)==NULL ) {
5432 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5433 } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) ) {
5434 mp_gr_stroke_ellipse(mp, p,true);
5436 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5437 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5439 if ( gr_post_script((mp_fill_object *)p)!=NULL ) {
5440 mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p));
5444 case mp_stroked_code:
5445 if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) )
5446 mp_gr_stroke_ellipse(mp, p,false);
5448 mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p));
5450 if ( gr_post_script((mp_stroked_object *)p)!=NULL ) {
5451 mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p));
5456 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5458 scf=mp_gr_choose_scale(mp, p);
5460 scf=mp_indexed_size(mp, gr_font_n(p), gr_name_type(p));
5461 @<Shift or transform as necessary before outputting text node~|p| at scale
5462 factor~|scf|; set |transformed:=true| if the original transformation must
5464 mp_ps_string_out(mp, gr_text_p(p));
5465 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5466 @<Print the size information and \ps\ commands for text node~|p|@>;
5469 if ( gr_post_script((mp_text_object *)p)!=NULL ) {
5470 mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp);
5473 case mp_start_clip_code:
5474 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5475 mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p));
5476 mp_ps_print_cmd(mp, " clip"," W");
5478 if ( mp->internal[mp_restore_clip_color]>0 )
5479 mp_gs_unknown_graphics_state(mp, 1);
5481 case mp_stop_clip_code:
5482 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5484 if ( mp->internal[mp_restore_clip_color]>0 )
5485 mp_gs_unknown_graphics_state(mp, 2);
5487 mp_gs_unknown_graphics_state(mp, -1);
5489 case mp_start_bounds_code:
5490 case mp_stop_bounds_code:
5492 case mp_special_code:
5494 mp_special_object *ps = (mp_special_object *)p;
5495 mp_ps_print_nl (mp, gr_pre_script(ps));
5496 mp_ps_print_ln (mp);
5499 } /* all cases are enumerated */
5502 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
5503 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
5504 (mp->close_file)(mp,mp->ps_file);
5511 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) ;
5514 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) {
5515 return mp_gr_ship_out (hh, prologues, procset, true);
5523 @d do_write_prescript(a,b) {
5524 if ( (gr_pre_script((b *)a))!=NULL ) {
5525 mp_ps_print_nl (mp, gr_pre_script((b *)a));
5530 @<Write |pre_script| of |p|@>=
5532 if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); }
5533 else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); }
5534 else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); }
5537 @ The envelope of a cyclic path~|q| could be computed by calling
5538 |make_envelope| once for |q| and once for its reversal. We don't do this
5539 because it would fail color regions that are covered by the pen regardless
5540 of where it is placed on~|q|.
5542 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5543 if ( gr_left_type(q)!=mp_endpoint ) {
5544 gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5545 gr_right_type(q)=mp_endpoint;
5550 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5552 mp_ps_print_char(mp, ' ');
5553 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5554 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5555 mp_ps_print(mp, " fshow");
5557 mp_ps_print_cmd(mp, " grestore"," Q")
5561 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5562 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5563 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5564 if ( transformed ) {
5565 mp_ps_print_cmd(mp, "gsave [", "q [");
5566 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5567 mp_make_scaled(mp, gr_tyx_val(p),scf));
5568 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5569 mp_make_scaled(mp, gr_tyy_val(p),scf));
5570 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5571 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5573 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5574 mp_ps_print_cmd(mp, "moveto","m");
5579 void mp_gr_toss_objects ( mp_edge_object *hh) ;
5580 void mp_gr_toss_object (mp_graphic_object *p) ;
5583 void mp_gr_toss_object (mp_graphic_object *p) {
5585 mp_stroked_object *ts;
5587 switch (gr_type(p)) {
5589 tf = (mp_fill_object *)p;
5590 mp_xfree(gr_pre_script(tf));
5591 mp_xfree(gr_post_script(tf));
5592 mp_gr_toss_knot_list(mp,gr_pen_p(tf));
5593 mp_gr_toss_knot_list(mp,gr_path_p(tf));
5594 mp_gr_toss_knot_list(mp,gr_htap_p(p));
5596 case mp_stroked_code:
5597 ts = (mp_stroked_object *)p;
5598 mp_xfree(gr_pre_script(ts));
5599 mp_xfree(gr_post_script(ts));
5600 mp_gr_toss_knot_list(mp,gr_pen_p(ts));
5601 mp_gr_toss_knot_list(mp,gr_path_p(ts));
5602 if (gr_dash_p(p)!=NULL)
5603 mp_gr_toss_dashes (mp,gr_dash_p(p));
5606 tt = (mp_text_object *)p;
5607 mp_xfree(gr_pre_script(tt));
5608 mp_xfree(gr_post_script(tt));
5609 mp_xfree(gr_text_p(p));
5610 mp_xfree(gr_font_name(p));
5612 case mp_start_clip_code:
5613 mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p));
5615 case mp_start_bounds_code:
5616 mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p));
5618 case mp_stop_clip_code:
5619 case mp_stop_bounds_code:
5621 case mp_special_code:
5622 mp_xfree(gr_pre_script((mp_special_object *)p));
5624 } /* all cases are enumerated */
5630 void mp_gr_toss_objects (mp_edge_object *hh) {
5631 mp_graphic_object *p, *q;
5635 mp_gr_toss_object(p);
5642 mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ;
5646 mp_gr_copy_object (MP mp, mp_graphic_object *p) {
5648 mp_stroked_object *ts;
5651 mp_bounds_object *tb;
5652 mp_special_object *tp;
5653 mp_graphic_object *q = NULL;
5654 switch (gr_type(p)) {
5656 tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code);
5657 gr_pre_script(tf) = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p));
5658 gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p));
5659 gr_path_p(tf) = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p));
5660 gr_htap_p(tf) = mp_gr_copy_path(mp,gr_htap_p(p));
5661 gr_pen_p(tf) = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p));
5662 q = (mp_graphic_object *)tf;
5664 case mp_stroked_code:
5665 ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code);
5666 gr_pre_script(ts) = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p));
5667 gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p));
5668 gr_path_p(ts) = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p));
5669 gr_pen_p(ts) = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p));
5670 gr_dash_p(ts) = mp_gr_copy_dashes(mp,gr_dash_p(p));
5671 q = (mp_graphic_object *)ts;
5674 tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code);
5675 gr_pre_script(tt) = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p));
5676 gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p));
5677 gr_text_p(tt) = mp_xstrdup(mp, gr_text_p(p));
5678 gr_font_name(tt) = mp_xstrdup(mp, gr_font_name(p));
5679 q = (mp_graphic_object *)tt;
5681 case mp_start_clip_code:
5682 tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code);
5683 gr_path_p(tc) = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p));
5684 q = (mp_graphic_object *)tc;
5686 case mp_start_bounds_code:
5687 tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code);
5688 gr_path_p(tb) = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p));
5689 q = (mp_graphic_object *)tb;
5691 case mp_special_code:
5692 tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code);
5693 gr_pre_script(tp) = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p));
5694 q = (mp_graphic_object *)tp;
5696 case mp_stop_clip_code:
5697 q = mp_new_graphic_object(mp, mp_stop_clip_code);
5699 case mp_stop_bounds_code:
5700 q = mp_new_graphic_object(mp, mp_stop_bounds_code);
5702 } /* all cases are enumerated */