1 % $Id: mp.web,v 1.8 2005/08/24 10:54:02 taco Exp $
2 % MetaPost, by John Hobby. Public domain.
4 % Much of this program was copied with permission from MF.web Version 1.9
5 % It interprets a language very similar to D.E. Knuth's METAFONT, but with
6 % changes designed to make it more suitable for PostScript output.
8 % TeX is a trademark of the American Mathematical Society.
9 % METAFONT is a trademark of Addison-Wesley Publishing Company.
10 % PostScript is a trademark of Adobe Systems Incorporated.
12 % Here is TeX material that gets inserted after \input webmac
13 \def\hang{\hangindent 3em\noindent\ignorespaces}
14 \def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
17 \def\ph{\hbox{Pascal-H}}
18 \def\psqrt#1{\sqrt{\mathstrut#1}}
20 \def\pct!{{\char`\%}} % percent sign in ordinary text
21 \font\tenlogo=logo10 % font used for the METAFONT logo
23 \def\MF{{\tenlogo META}\-{\tenlogo FONT}}
24 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
25 \def\<#1>{$\langle#1\rangle$}
26 \def\section{\mathhexbox278}
27 \let\swap=\leftrightarrow
28 \def\round{\mathop{\rm round}\nolimits}
29 \mathchardef\vb="026A % synonym for `\|'
30 \def\[#1]{} % from pascal web
31 \def\(#1){} % this is used to make section names sort themselves better
32 \def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
34 \let\?=\relax % we want to be able to \write a \?
36 \def\title{MetaPost \ps\ output}
37 \def\topofcontents{\hsize 5.5in
38 \vglue -30pt plus 1fil minus 1.5in
39 \def\?##1]{\hbox to 1in{\hfil##1.\ }}
41 \def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
50 @d unity 0200000 /* $2^{16}$, represents 1.00000 */
51 @d el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
52 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
53 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
54 @d negate(A) (A)=-(A) /* change the sign of a variable */
57 @d print_err(A) mp_print_err(mp,(A))
58 @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
68 #include "mpmp.h" /* internal header */
69 #include "mppsout.h" /* internal header */
72 @<Static variables in the outer block@>;
74 @ There is a small bit of code from the backend that bleads through
75 to the frontend because I do not know how to set up the includes
76 properly. Those are the definitions of |struct libavl_allocator|
77 and |typedef struct psout_data_struct * psout_data|.
79 The |libavl_allocator| is a trick that makes sure that frontends
80 do not need |avl.h|, and the |psout_data| is needed for the backend
85 typedef struct psout_data_struct {
88 @<Exported function headers@>
90 @ @<Exported function headers@>=
91 void mp_backend_initialize (MP mp) ;
92 void mp_backend_free (MP mp) ;
95 @c void mp_backend_initialize (MP mp) {
96 mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
97 @<Set initial values@>;
99 void mp_backend_free (MP mp) {
100 @<Dealloc variables@>;
108 @ Writing to ps files
112 /* the number of characters on the current \ps\ file line */
114 @ @<Set initial values@>=
115 mp->ps->ps_offset = 0;
119 @d wps(A) (mp->write_ascii_file)(mp,mp->ps_file,(A))
123 (mp->write_ascii_file)(mp,mp->ps_file,(char *)ss);
125 @d wps_cr (mp->write_ascii_file)(mp,mp->ps_file,"\n")
126 @d wps_ln(A) { wterm_cr; (mp->write_ascii_file)(mp,mp->ps_file,(A)); }
129 void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
135 void mp_ps_print_char (MP mp, ASCII_code s) { /* prints a single character */
137 wps_cr; mp->ps->ps_offset=0;
139 wps_chr(s); incr(mp->ps->ps_offset);
144 void mp_ps_do_print (MP mp, char *ss, unsigned int len) { /* prints string |s| */
147 mp_ps_print_char(mp, ss[j]); incr(j);
151 @ Deciding where to break the ps output line.
153 @d ps_room(A) if ( (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) {
154 mp_ps_print_ln(mp); /* optional line break */
158 void mp_ps_print (MP mp, char *ss) {
160 mp_ps_do_print(mp, ss, strlen(ss));
163 @ The procedure |print_nl| is like |print|, but it makes sure that the
164 string appears at the beginning of a new line.
167 void mp_ps_print_nl (MP mp, char *s) { /* prints string |s| at beginning of line */
168 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
172 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
175 void mp_ps_print_the_digs (MP mp, eight_bits k) {
176 /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
178 mp_ps_print_char(mp, '0'+mp->dig[k]);
182 @ The following procedure, which prints out the decimal representation of a
183 given integer |n|, has been written carefully so that it works properly
184 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
185 to negative arguments, since such operations are not implemented consistently
186 by all \PASCAL\ compilers.
189 void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */
190 integer m; /* used to negate |n| in possibly dangerous cases */
191 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
193 mp_ps_print_char(mp, '-');
194 if ( n>-100000000 ) {
197 m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
201 mp->dig[0]=0; incr(n);
206 mp->dig[k]=n % 10; n=n / 10; incr(k);
208 mp_ps_print_the_digs(mp, k);
212 void mp_ps_print_int (MP mp,integer n);
214 @ \MP\ also makes use of a trivial procedure to print two digits. The
215 following subroutine is usually called with a parameter in the range |0<=n<=99|.
218 void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */
220 mp_ps_print_char(mp, '0'+(n / 10));
221 mp_ps_print_char(mp, '0'+(n % 10));
224 @ Conversely, here is a procedure analogous to |print_int|. If the output
225 of this procedure is subsequently read by \MP\ and converted by the
226 |round_decimals| routine above, it turns out that the original value will
227 be reproduced exactly. A decimal point is printed only if the value is
228 not an integer. If there is more than one way to print the result with
229 the optimum number of digits following the decimal point, the closest
230 possible value is given.
232 The invariant relation in the \&{repeat} loop is that a sequence of
233 decimal digits yet to be printed will yield the original number if and only if
234 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
235 We can stop if and only if $f=0$ satisfies this condition; the loop will
236 terminate before $s$ can possibly become zero.
239 void mp_ps_print_scaled (MP mp,scaled s) {
240 scaled delta; /* amount of allowable inaccuracy */
242 mp_ps_print_char(mp, '-');
243 negate(s); /* print the sign, if negative */
245 mp_ps_print_int(mp, s / unity); /* print the integer part */
249 mp_ps_print_char(mp, '.');
252 s=s+0100000-(delta / 2); /* round the final digit */
253 mp_ps_print_char(mp, '0'+(s / unity));
261 @* \[44a] Dealing with font encodings.
263 First, here are a few helpers for parsing files
265 @d check_buf(size, buf_size)
266 if ((unsigned)(size) > (unsigned)(buf_size)) {
268 snprintf(s,128,"buffer overflow: (%d,%d) at file %s, line %d",
269 size,buf_size, __FILE__, __LINE__ );
270 mp_fatal_error(mp,s);
273 @d append_char_to_buf(c, p, buf, buf_size) do {
276 if (c == 13 || c == EOF)
278 if (c != ' ' || (p > buf && p[-1] != 32)) {
279 check_buf(p - buf + 1, (buf_size));
284 @d append_eol(p, buf, buf_size) do {
285 check_buf(p - buf + 2, (buf_size));
286 if (p - buf > 1 && p[-1] != 10)
288 if (p - buf > 2 && p[-2] == 32) {
295 @d remove_eol(p, buf) do {
301 @d skip(p, c) if (*p == c) p++
302 @d strend(s) strchr(s, 0)
303 @d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
308 boolean loaded; /* the encoding has been loaded? */
309 char *file_name; /* encoding file name */
310 char *enc_name; /* encoding true name */
311 integer objnum; /* object number */
313 integer tounicode; /* object number of associated ToUnicode entry */
323 #define ENC_BUF_SIZE 0x1000
324 char enc_line[ENC_BUF_SIZE];
328 @d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file)
329 @d enc_close() (mp->close_file)(mp,mp->ps->enc_file)
332 int enc_getchar(MP mp) {
334 unsigned char byte=0;
335 void *byte_ptr = &byte;
336 (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len);
341 static boolean mp_enc_open (MP mp, char *n) {
342 mp->ps->enc_file=(mp->open_file)(mp,n, "rb", mp_filetype_encoding);
343 if (mp->ps->enc_file!=NULL)
348 static void mp_enc_getline (MP mp) {
353 print_err("unexpected end of file");
356 p = mp->ps->enc_line;
358 c = enc_getchar (mp);
359 append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
361 append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
362 if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
365 static void mp_load_enc (MP mp, char *enc_name,
366 char **enc_encname, char **glyph_names){
367 char buf[ENC_BUF_SIZE], *p, *r;
370 int save_selector = mp->selector;
371 if (!mp_enc_open (mp,enc_name)) {
372 mp_print (mp,"cannot open encoding file for reading");
375 mp_normalize_selector(mp);
377 mp_print (mp, enc_name);
379 if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
380 remove_eol (r, mp->ps->enc_line);
381 print_err ("invalid encoding vector (a name or `[' missing): `");
382 mp_print(mp,mp->ps->enc_line);
386 while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
387 myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
388 memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
389 *(myname+(r-mp->ps->enc_line-1))=0;
390 *enc_encname = myname;
398 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
401 if (names_count > 256) {
402 print_err ("encoding vector contains more than 256 names");
405 if (mp_xstrcmp (buf, notdef) != 0)
406 glyph_names[names_count] = mp_xstrdup (mp,buf);
409 if (*r != 10 && *r != '%') {
410 if (str_prefix (r, "] def"))
413 remove_eol (r, mp->ps->enc_line);
415 ("invalid encoding vector: a name or `] def' expected: `");
416 mp_print(mp,mp->ps->enc_line);
422 r = mp->ps->enc_line;
427 mp->selector = save_selector;
429 static void mp_read_enc (MP mp, enc_entry * e) {
433 mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
437 @ |write_enc| is used to write either external encoding (given in map file) or
438 internal encoding (read from the font file); when |glyph_names| is NULL
439 the 2nd argument is a pointer to the encoding entry; otherwise the 3rd is
440 the object number of the Encoding object
443 static void mp_write_enc (MP mp, char **glyph_names, enc_entry * e) {
448 if (glyph_names == NULL) {
449 if (e->objnum != 0) /* the encoding has been written already */
457 mp_ps_print(mp,"\n%%%%BeginResource: encoding ");
458 mp_ps_print(mp, e->enc_name);
459 mp_ps_print_nl(mp, "/");
460 mp_ps_print(mp, e->enc_name);
461 mp_ps_print(mp, " [ ");
462 foffset = strlen(e->file_name)+3;
463 for (i = 0; i < 256; i++) {
465 if (s+1+foffset>=80) {
470 mp_ps_print_char(mp,'/');
471 mp_ps_print(mp, g[i]);
472 mp_ps_print_char(mp,' ');
476 mp_ps_print_nl (mp,"] def\n");
477 mp_ps_print(mp,"%%%%EndResource");
481 @ All encoding entries go into AVL tree for fast search by name.
484 struct avl_table *enc_tree;
486 @ Memory management functions for avl
488 @<Static variables in the outer block@>=
489 static const char notdef[] = ".notdef";
492 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
493 static void avl_xfree (struct libavl_allocator *allocator, void *block);
496 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
498 return malloc (size);
500 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
506 struct libavl_allocator avl_xallocator;
508 @ @<Set initial...@>=
509 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
510 mp->ps->avl_xallocator.libavl_free= avl_xfree;
511 mp->ps->enc_tree = NULL;
514 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
516 return strcmp (((const enc_entry *) pa)->file_name,
517 ((const enc_entry *) pb)->file_name);
519 static enc_entry * mp_add_enc (MP mp, char *s) {
523 if (mp->ps->enc_tree == NULL) {
524 mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
527 p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
528 if (p != NULL) /* encoding already registered */
530 p = mp_xmalloc (mp,1,sizeof (enc_entry));
532 p->file_name = mp_xstrdup (mp,s);
535 p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
536 for (i = 0; i < 256; i++)
537 p->glyph_names[i] = (char *) notdef;
538 aa = avl_probe (mp->ps->enc_tree, p);
545 static void mp_destroy_enc_entry (void *pa, void *pb) {
549 p = (enc_entry *) pa;
551 mp_xfree (p->file_name);
552 if (p->glyph_names != NULL)
553 for (i = 0; i < 256; i++)
554 if (p->glyph_names[i] != notdef)
555 mp_xfree (p->glyph_names[i]);
556 mp_xfree (p->glyph_names);
561 static void enc_free (MP mp);
563 @ @c static void enc_free (MP mp) {
564 if (mp->ps->enc_tree != NULL)
565 avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
568 @ @<Exported function headers@>=
569 void mp_reload_encodings (MP mp) ;
572 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) ;
574 @ @c void mp_reload_encodings (MP mp) {
578 int lastfnum = mp->last_fnum;
579 for (f=null_font+1;f<=lastfnum;f++) {
580 if (mp->font_enc_name[f]!=NULL ) {
581 mp_xfree(mp->font_enc_name[f]);
582 mp->font_enc_name[f]=NULL;
584 if (mp_has_fm_entry (mp,f,&fm_cur)) {
585 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
586 e = fm_cur->encoding;
592 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) {
596 for (f=null_font+1;f<=lastfnum;f++) {
597 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
598 if (fm != NULL && (fm->ps_name != NULL)) {
599 if (is_reencoded (fm)) {
600 if (encodings_only || (!is_subsetted (fm))) {
602 mp_write_enc (mp,NULL, e);
603 /* clear for next run */
612 @* \[44b] Parsing font map files.
620 @d fm_close() (mp->close_file)(mp,mp->ps->fm_file)
621 @d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file)
624 int fm_getchar(MP mp) {
626 unsigned char byte=0;
627 void *byte_ptr = &byte;
628 (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
636 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
637 enum _ltype { MAPFILE, MAPLINE };
638 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
639 typedef struct mitem {
640 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
641 int type; /* map file or map line */
642 char *map_line; /* pointer to map file name or map line */
643 int lineno; /* line number in map file */
649 fm_entry *loaded_tfm_found;
650 fm_entry *avail_tfm_found;
651 fm_entry *non_tfm_found;
652 fm_entry *not_avail_tfm_found;
654 @ @<Set initial...@>=
655 mp->ps->mitem = NULL;
658 static const char nontfm[] = "<nontfm>";
661 @d read_field(r, q, buf) do {
663 while (*r != ' ' && *r != '\0')
671 fm->F = mp_xstrdup(mp,buf);
683 static fm_entry *new_fm_entry (MP mp) {
685 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
690 fm->subset_tag = NULL;
692 fm->tfm_num = null_font;
693 fm->tfm_avail = TFM_UNCHECKED;
701 fm->all_glyphs = false;
708 static void delete_fm_entry (fm_entry * fm) {
709 mp_xfree (fm->tfm_name);
710 mp_xfree (fm->ps_name);
711 mp_xfree (fm->ff_name);
712 mp_xfree (fm->subset_tag);
713 mp_xfree (fm->charset);
717 static ff_entry *new_ff_entry (MP mp) {
719 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
725 static void delete_ff_entry (ff_entry * ff) {
726 mp_xfree (ff->ff_name);
727 mp_xfree (ff->ff_path);
731 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
732 static char buf[SMALL_BUF_SIZE];
733 char *p = tfmname, *r = strend (p) - 1, *q = r;
734 while (q > p && isdigit (*q))
736 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
738 check_buf (q - p + 1, SMALL_BUF_SIZE);
739 strncpy (buf, p, (size_t) (q - p));
745 @ @<Exported function headers@>=
746 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
749 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
750 fm_entry *res = NULL;
751 res = mp_fm_lookup (mp, f);
755 return (res != NULL);
759 struct avl_table *tfm_tree;
760 struct avl_table *ps_tree;
761 struct avl_table *ff_tree;
763 @ @<Set initial...@>=
764 mp->ps->tfm_tree = NULL;
765 mp->ps->ps_tree = NULL;
766 mp->ps->ff_tree = NULL;
768 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
771 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
773 return strcmp (((const fm_entry *) pa)->tfm_name,
774 ((const fm_entry *) pb)->tfm_name);
777 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
779 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
781 const fm_entry *p1 = (const fm_entry *) pa, *p2 = (const fm_entry *) pb;
783 assert (p1->ps_name != NULL && p2->ps_name != NULL);
784 if ((i = strcmp (p1->ps_name, p2->ps_name)))
786 cmp_return (p1->slant, p2->slant);
787 cmp_return (p1->extend, p2->extend);
788 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
789 (i = strcmp (p1->tfm_name, p2->tfm_name)))
794 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
796 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
798 return strcmp (((const ff_entry *) pa)->ff_name,
799 ((const ff_entry *) pb)->ff_name);
802 @ @c static void create_avl_trees (MP mp) {
803 if (mp->ps->tfm_tree == NULL) {
804 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
805 assert (mp->ps->tfm_tree != NULL);
807 if (mp->ps->ps_tree == NULL) {
808 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
809 assert (mp->ps->ps_tree != NULL);
811 if (mp->ps->ff_tree == NULL) {
812 mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
813 assert (mp->ps->ff_tree != NULL);
817 @ The function |avl_do_entry| is not completely symmetrical with regards
818 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
819 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
820 with the original version.
824 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
825 @d set_pslink(fm) ((fm)->links |= LINK_PS)
826 @d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
827 @d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
828 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
829 @d has_pslink(fm) ((fm)->links & LINK_PS)
832 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
838 /* handle |tfm_name| link */
840 if (strcmp (fp->tfm_name, nontfm)) {
841 p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
843 if (mode == FM_DUPIGNORE) {
844 snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
848 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
849 if (mp_has_font_size(mp,p->tfm_num)) {
851 "fontmap entry for `%s' has been used, replace/delete not allowed",
856 a = avl_delete (mp->ps->tfm_tree, p);
863 if (mode != FM_DELETE) {
864 aa = avl_probe (mp->ps->tfm_tree, fp);
870 /* handle |ps_name| link */
872 if (fp->ps_name != NULL) {
873 assert (fp->tfm_name != NULL);
874 p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
876 if (mode == FM_DUPIGNORE) {
878 "ps_name entry for `%s' already exists, duplicates ignored",
882 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
883 if (mp_has_font_size(mp,p->tfm_num)) {
884 /* REPLACE/DELETE not allowed */
886 "fontmap entry for `%s' has been used, replace/delete not allowed",
891 a = avl_delete (mp->ps->ps_tree, p);
894 if (!has_tfmlink (p))
898 if (mode != FM_DELETE) {
899 aa = avl_probe (mp->ps->ps_tree, fp);
905 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
906 return 1; /* deallocation of |fm_entry| structure required */
911 @ consistency check for map entry, with warn flag
914 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
918 if (fm->ps_name != NULL) {
919 if (is_basefont (fm)) {
920 if (is_fontfile (fm) && !is_included (fm)) {
922 snprintf(s,128, "invalid entry for `%s': "
923 "font file must be included or omitted for base fonts",
929 } else { /* not a base font */
930 /* if no font file given, drop this entry */
931 /* |if (!is_fontfile (fm)) {
934 "invalid entry for `%s': font file missing",
943 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
946 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
952 if ((fm->slant != 0 || fm->extend != 0) &&
953 (is_truetype (fm))) {
956 "invalid entry for `%s': "
957 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
963 if (abs (fm->slant) > 1000) {
966 "invalid entry for `%s': too big value of SlantFont (%g)",
967 fm->tfm_name, fm->slant / 1000.0);
972 if (abs (fm->extend) > 2000) {
975 "invalid entry for `%s': too big value of ExtendFont (%g)",
976 fm->tfm_name, fm->extend / 1000.0);
982 !(is_truetype (fm) && is_included (fm) &&
983 is_subsetted (fm) && !is_reencoded (fm))) {
986 "invalid entry for `%s': "
987 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
996 @ returns true if s is one of the 14 std. font names; speed-trimmed.
998 @c static boolean check_basefont (char *s) {
999 static const char *basefont_names[] = {
1000 "Courier", /* 0:7 */
1001 "Courier-Bold", /* 1:12 */
1002 "Courier-Oblique", /* 2:15 */
1003 "Courier-BoldOblique", /* 3:19 */
1004 "Helvetica", /* 4:9 */
1005 "Helvetica-Bold", /* 5:14 */
1006 "Helvetica-Oblique", /* 6:17 */
1007 "Helvetica-BoldOblique", /* 7:21 */
1009 "Times-Roman", /* 9:11 */
1010 "Times-Bold", /* 10:10 */
1011 "Times-Italic", /* 11:12 */
1012 "Times-BoldItalic", /* 12:16 */
1013 "ZapfDingbats" /* 13:12 */
1015 static const int Index[] =
1016 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1019 const size_t n = strlen (s);
1023 if (n == 12) { /* three names have length 12 */
1026 k = 1; /* Courier-Bold */
1029 k = 11; /* Times-Italic */
1032 k = 13; /* ZapfDingbats */
1039 if (k > -1 && !strcmp (basefont_names[k], s))
1045 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1047 @c static void fm_scan_line (MP mp) {
1048 int a, b, c, j, u = 0, v = 0;
1051 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1052 char *p, *q, *r, *s;
1054 switch (mp->ps->mitem->type) {
1058 c = fm_getchar (mp);
1059 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1065 r = mp->ps->mitem->map_line;
1070 if (*r == '\0' || is_cfg_comment (*r))
1072 fm = new_fm_entry (mp);
1073 read_field (r, q, buf);
1074 set_field (tfm_name);
1076 read_field (r, q, buf);
1077 if (*buf != '<' && *buf != '"')
1078 set_field (ps_name);
1080 r = p; /* unget the field */
1081 if (isdigit (*r)) { /* font flags given */
1082 fm->flags = atoi (r);
1083 while (isdigit (*r))
1086 while (1) { /* loop through "specials", encoding, font file */
1091 case '"': /* opening quote */
1096 if (sscanf (r, "%f %n", &d, &j) > 0) {
1097 s = r + j; /* jump behind number, eat also blanks, if any */
1098 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1099 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1100 if (str_prefix (s, "SlantFont")) {
1101 d *= 1000.0; /* correct rounding also for neg. numbers */
1102 fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1103 r = s + strlen ("SlantFont");
1104 } else if (str_prefix (s, "ExtendFont")) {
1106 fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1107 if (fm->extend == 1000)
1109 r = s + strlen ("ExtendFont");
1110 } else { /* unknown name */
1112 *r != ' ' && *r != '"' && *r != '\0';
1113 r++); /* jump over name */
1114 c = *r; /* remember char for temporary end of string */
1116 snprintf(warn_s,128,
1117 "invalid entry for `%s': unknown name `%s' ignored",
1123 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1126 if (*r == '"') /* closing quote */
1129 snprintf(warn_s,128,
1130 "invalid entry for `%s': closing quote missing",
1136 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1137 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1143 default: /* encoding or font file specification */
1147 if (*r == '<' || *r == '[')
1150 read_field (r, q, buf);
1151 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1152 if (strlen (buf) > 4 && strcasecmp (strend (buf) - 4, ".enc") == 0) {
1153 fm->encoding = mp_add_enc (mp, buf);
1154 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1155 } else if (strlen (buf) > 0) { /* file name given */
1156 /* font file, formats:
1157 * subsetting: '<cmr10.pfa'
1158 * no subsetting: '<<cmr10.pfa'
1159 * no embedding: 'cmr10.pfa'
1161 if (a == '<' || u == '<') {
1163 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1165 /* otherwise b == '<' (or '[') => no subsetting */
1167 set_field (ff_name);
1176 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1178 if (is_fontfile (fm)
1179 && strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1181 if (check_fm_entry (mp,fm, true) != 0)
1184 Until here the map line has been completely scanned without errors;
1185 fm points to a valid, freshly filled-out |fm_entry| structure.
1186 Now follows the actual work of registering/deleting.
1188 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
1191 delete_fm_entry (fm);
1195 @c static void fm_read_info (MP mp) {
1198 if (mp->ps->tfm_tree == NULL)
1199 create_avl_trees (mp);
1200 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1202 mp->ps->mitem->lineno = 1;
1203 switch (mp->ps->mitem->type) {
1205 n = mp->ps->mitem->map_line;
1206 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1207 if (!mp->ps->fm_file) {
1208 snprintf(s,256,"cannot open font map file %s",n);
1211 int save_selector = mp->selector;
1212 mp_normalize_selector(mp);
1215 while (!fm_eof ()) {
1217 mp->ps->mitem->lineno++;
1221 mp->selector = save_selector;
1222 mp->ps->fm_file = NULL;
1232 mp->ps->mitem->map_line = NULL; /* done with this line */
1237 static scaled mp_round_xn_over_d (MP mp, scaled x, integer n, integer d) {
1238 boolean positive; /* was |x>=0|? */
1239 unsigned int t,u; /* intermediate quantities */
1240 integer v; /* intermediate quantities */
1244 negate(x); positive=false;
1247 u=(x / 0100000)*n+(t / 0100000);
1248 v=(u % d)*0100000 + (t % 0100000);
1249 if ( u / d>=0100000 ) mp->arith_error=true;
1250 else u=0100000*(u / d) + (v / d);
1254 return ( positive ? u : -u );
1256 static fm_entry *mk_ex_fm (MP mp, font_number f, fm_entry * basefm, int ex) {
1258 integer e = basefm->extend;
1261 fm = new_fm_entry (mp);
1262 fm->flags = basefm->flags;
1263 fm->encoding = basefm->encoding;
1264 fm->type = basefm->type;
1265 fm->slant = basefm->slant;
1266 fm->extend = mp_round_xn_over_d (mp, e, 1000 + ex, 1000);
1267 /* modify ExtentFont to simulate expansion */
1268 if (fm->extend == 1000)
1270 fm->tfm_name = mp_xstrdup (mp,mp->font_name[f]);
1271 if (basefm->ps_name != NULL)
1272 fm->ps_name = mp_xstrdup (mp,basefm->ps_name);
1273 fm->ff_name = mp_xstrdup (mp,basefm->ff_name);
1276 fm->tfm_avail = TFM_FOUND;
1277 assert (strcmp (fm->tfm_name, nontfm));
1281 @ @c static void init_fm (fm_entry * fm, font_number f) {
1282 if (fm->tfm_num == null_font ) {
1284 fm->tfm_avail = TFM_FOUND;
1289 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1292 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1294 fm_entry *fm, *exfm;
1297 if (mp->ps->tfm_tree == NULL)
1298 fm_read_info (mp); /* only to read default map file */
1299 tfm = mp->font_name[f];
1300 assert (strcmp (tfm, nontfm));
1301 /* Look up for full <tfmname>[+-]<expand> */
1303 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1306 return (fm_entry *) fm;
1308 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1309 if (tfm == NULL) /* not an expanded font, nothing to do */
1313 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1314 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1315 return (fm_entry *) fm; /* font expansion uses the base font */
1316 /* the following code would be obsolete, as would be |mk_ex_fm| */
1317 if (!is_t1fontfile (fm) || !is_included (fm)) {
1320 "font %s cannot be expanded (not an included Type1 font)", tfm);
1324 exfm = mk_ex_fm (mp, f, fm, e); /* copies all fields from fm except tfm name */
1326 ai = avl_do_entry (mp, exfm, FM_DUPIGNORE);
1328 return (fm_entry *) exfm;
1333 @ Early check whether a font file exists. Used e. g. for replacing fonts
1334 of embedded PDF files: Without font file, the font within the embedded
1335 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1336 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1337 only once per font file name + expansion parameter. This might help
1338 keeping speed, if many PDF pages with same fonts are to be embedded.
1340 The |ff_tree| contains only font files, which are actually needed,
1341 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1344 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1349 assert (fm->ff_name != NULL);
1350 tmp.ff_name = fm->ff_name;
1351 ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1352 if (ff == NULL) { /* not yet in database */
1353 ff = new_ff_entry (mp);
1354 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1355 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1356 aa = avl_probe (mp->ps->ff_tree, ff);
1357 assert (aa != NULL);
1362 @ Process map file given by its name or map line contents. Items not
1363 beginning with [+-=] flush default map file, if it has not yet been
1364 read. Leading blanks and blanks immediately following [+-=] are ignored.
1367 @c static void mp_process_map_item (MP mp, char *s, int type) {
1371 s++; /* ignore leading blank */
1373 case '+': /* +mapfile.map, +mapline */
1374 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1377 case '=': /* =mapfile.map, =mapline */
1378 mode = FM_REPLACE; /* try to replace earlier entry */
1381 case '-': /* -mapfile.map, -mapline */
1382 mode = FM_DELETE; /* try to delete entry */
1386 mode = FM_DUPIGNORE; /* like +, but also: */
1387 mp_xfree(mp->ps->mitem->map_line);
1388 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1391 s++; /* ignore blank after [+-=] */
1392 p = s; /* map item starts here */
1394 case MAPFILE: /* remove blank at end */
1395 while (*p != '\0' && *p != ' ')
1399 case MAPLINE: /* blank at end allowed */
1404 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1406 if (*s != '\0') { /* only if real item to process */
1407 mp->ps->mitem->mode = mode;
1408 mp->ps->mitem->type = type;
1409 mp->ps->mitem->map_line = s;
1414 @ @<Exported function headers@>=
1415 void mp_map_file (MP mp, str_number t);
1416 void mp_map_line (MP mp, str_number t);
1417 void mp_init_map_file (MP mp, int is_troff);
1420 void mp_map_file (MP mp, str_number t) {
1421 char *s = mp_xstrdup(mp,mp_str (mp,t));
1422 mp_process_map_item (mp, s, MAPFILE);
1425 void mp_map_line (MP mp, str_number t) {
1426 char *s = mp_xstrdup(mp,mp_str (mp,t));
1427 mp_process_map_item (mp, s, MAPLINE);
1432 @c void mp_init_map_file (MP mp, int is_troff) {
1434 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1435 mp->ps->mitem->mode = FM_DUPIGNORE;
1436 mp->ps->mitem->type = MAPFILE;
1437 mp->ps->mitem->map_line = NULL;
1438 r = (mp->find_file)(mp,"mpost.map", "rb", mp_filetype_fontmap);
1441 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1444 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1446 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1451 @ @<Dealloc variables@>=
1452 if (mp->ps->mitem!=NULL) {
1453 mp_xfree(mp->ps->mitem->map_line);
1454 mp_xfree(mp->ps->mitem);
1460 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1463 fm = (fm_entry *) pa;
1464 if (!has_pslink (fm))
1465 delete_fm_entry (fm);
1469 static void destroy_fm_entry_ps (void *pa, void *pb) {
1472 fm = (fm_entry *) pa;
1473 if (!has_tfmlink (fm))
1474 delete_fm_entry (fm);
1478 static void destroy_ff_entry (void *pa, void *pb) {
1481 ff = (ff_entry *) pa;
1482 delete_ff_entry (ff);
1486 static void fm_free (MP mp);
1489 static void fm_free (MP mp) {
1490 if (mp->ps->tfm_tree != NULL)
1491 avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1492 if (mp->ps->ps_tree != NULL)
1493 avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1494 if (mp->ps->ff_tree != NULL)
1495 avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1498 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1499 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1500 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
1501 one or more spaces, a PostScript name, and possibly a space and some other junk.
1502 This routine reads the table, updates |font_ps_name| entries starting after
1503 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
1505 @d ps_tab_name "psfonts.map" /* locates font name translation table */
1508 static void mp_read_psname_table (MP mp) ;
1511 static void mp_read_psname_table (MP mp) {
1513 if (mp->ps->mitem == NULL) {
1514 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1515 mp->ps->mitem->mode = FM_DUPIGNORE;
1516 mp->ps->mitem->type = MAPFILE;
1517 mp->ps->mitem->map_line = NULL;
1519 mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name);
1521 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1522 if (mp_has_fm_entry(mp, k, NULL)) {
1523 mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1526 mp->last_ps_fnum=mp->last_fnum;
1530 @ The traditional function is a lot shorter now.
1534 @* \[44c] Helper functions for Type1 fonts.
1537 typedef char char_entry;
1538 typedef unsigned char Byte;
1542 char_entry *char_ptr, *char_array;
1544 char *job_id_string;
1546 @ @<Set initial...@>=
1547 mp->ps->char_array = NULL;
1548 mp->ps->job_id_string = NULL;
1551 @d SMALL_ARRAY_SIZE 256
1555 void mp_set_job_id (MP mp) {
1556 char *name_string, *format_string, *s;
1559 if (mp->ps->job_id_string != NULL)
1561 if ( mp->job_name==NULL )
1562 mp->job_name = mp_xstrdup(mp,"mpout");
1563 name_string = mp_xstrdup (mp,mp->job_name);
1564 format_string = mp_xstrdup (mp,mp->mem_ident);
1565 slen = SMALL_BUF_SIZE +
1566 strlen (name_string) +
1567 strlen (format_string);
1568 s = mp_xmalloc (mp,slen, sizeof (char));
1569 i = snprintf (s, slen,
1570 "%.4d/%.2d/%.2d %.2d:%.2d %s %s",
1571 (mp->internal[mp_year]>>16),
1572 (mp->internal[mp_month]>>16),
1573 (mp->internal[mp_day]>>16),
1574 (mp->internal[mp_time]>>16) / 60,
1575 (mp->internal[mp_time]>>16) % 60,
1576 name_string, format_string);
1577 mp->ps->job_id_string = mp_xstrdup (mp,s);
1579 mp_xfree (name_string);
1580 mp_xfree (format_string);
1582 static void fnstr_append (MP mp, const char *s) {
1583 size_t l = strlen (s) + 1;
1584 alloc_array (char, l, SMALL_ARRAY_SIZE);
1585 strcat (mp->ps->char_ptr, s);
1586 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1589 @ @<Exported function headers@>=
1590 void mp_set_job_id (MP mp) ;
1592 @ @<Dealloc variables@>=
1593 mp_xfree(mp->ps->job_id_string);
1595 @ this is not really a true crc32, but it should be just enough to keep
1596 subsets prefixes somewhat disjunct
1599 static unsigned long crc32 (int oldcrc, const Byte *buf, int len) {
1600 unsigned long ret = 0;
1603 ret = (23<<24)+(45<<16)+(67<<8)+89;
1606 ret = (ret<<2)+buf[i];
1609 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1610 integer b; /* |char_base[f]| */
1612 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1618 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, int tex_font)
1624 if (mp->ps->job_id_string ==NULL)
1625 mp_fatal_error(mp, "no job id!");
1626 l = strlen (mp->ps->job_id_string) + 1;
1628 alloc_array (char, l, SMALL_ARRAY_SIZE);
1629 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1630 mp->ps->char_ptr = strend (mp->ps->char_array);
1631 if (fm_cur->tfm_name != NULL) {
1632 fnstr_append (mp," TFM name: ");
1633 fnstr_append (mp,fm_cur->tfm_name);
1635 fnstr_append (mp," PS name: ");
1636 if (fm_cur->ps_name != NULL)
1637 fnstr_append (mp,fm_cur->ps_name);
1638 fnstr_append (mp," Encoding: ");
1639 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1640 fnstr_append (mp,(fm_cur->encoding)->file_name);
1642 fnstr_append (mp,"built-in");
1643 fnstr_append (mp," CharSet: ");
1644 for (i = 0; i < 256; i++)
1645 if (mp_char_marked (mp,tex_font, i) && glyph_names[i] != notdef) {
1646 if (glyph_names[i]!=NULL) {
1647 fnstr_append (mp,"/");
1648 fnstr_append (mp,glyph_names[i]);
1651 if (fm_cur->charset != NULL) {
1652 fnstr_append (mp," Extra CharSet: ");
1653 fnstr_append (mp, fm_cur->charset);
1655 crc = crc32 (0L, Z_NULL, 0);
1656 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1657 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1658 * there are 26 uppercase chars ==> each char represents a number in range
1659 * |0..25|. The maximal number that can be represented by the tag is
1660 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1661 * of the CRC must be dropped out.
1663 for (i = 0; i < 6; i++) {
1664 tag[i] = 'A' + crc % 26;
1668 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1674 @d external_enc() (fm_cur->encoding)->glyph_names
1675 @d is_used_char(c) mp_char_marked (mp, tex_font, c)
1676 @d end_last_eexec_line()
1677 mp->ps->hexline_length = HEXLINE_WIDTH;
1679 mp->ps->t1_eexec_encrypt = false
1680 @d t1_log(s) mp_print(mp,(char *)s)
1681 @d t1_putchar(c) wps_chr(c)
1682 @d embed_all_glyphs(tex_font) false
1684 @d extra_charset() mp->ps->dvips_extra_charset
1685 @d update_subset_tag()
1686 @d fixedcontent true
1689 #define PRINTF_BUF_SIZE 1024
1690 char *dvips_extra_charset;
1692 unsigned char *grid;
1693 char *ext_glyph_names[256];
1694 char print_buf[PRINTF_BUF_SIZE];
1695 int t1_byte_waiting;
1697 @ @<Set initial ...@>=
1698 mp->ps->dvips_extra_charset=NULL;
1699 mp->ps->t1_byte_waiting=0;
1702 @d t1_ungetchar(A) mp->ps->t1_byte_waiting=A
1703 @d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file)
1704 @d t1_close() (mp->close_file)(mp,mp->ps->t1_file)
1705 @d valid_code(c) (c >= 0 && c < 256)
1708 int t1_getchar (MP mp) {
1710 unsigned char byte=0;
1711 void *byte_ptr = &byte;
1712 if (mp->ps->t1_byte_waiting) {
1713 byte = mp->ps->t1_byte_waiting;
1714 mp->ps->t1_byte_waiting = 0;
1716 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
1721 @ @<Static variables in the outer block@>=
1722 static const char *standard_glyph_names[256] =
1723 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1724 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1725 notdef, notdef, notdef, notdef, notdef, notdef,
1726 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1727 "space", "exclam", "quotedbl", "numbersign",
1728 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1729 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1730 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1731 "eight", "nine", "colon", "semicolon", "less",
1732 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1733 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1734 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1735 "backslash", "bracketright", "asciicircum", "underscore",
1736 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1737 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1738 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1739 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1740 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1741 notdef, notdef, notdef, notdef, notdef, notdef,
1742 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1743 notdef, notdef, notdef, "exclamdown", "cent",
1744 "sterling", "fraction", "yen", "florin", "section", "currency",
1745 "quotesingle", "quotedblleft", "guillemotleft",
1746 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1747 "dagger", "daggerdbl", "periodcentered", notdef,
1748 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1749 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1750 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1751 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1752 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1753 notdef, notdef, notdef, notdef, notdef, notdef,
1754 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1755 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1756 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1757 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1758 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1759 "germandbls", notdef, notdef, notdef, notdef };
1760 static const char charstringname[] = "/CharStrings";
1763 char **t1_glyph_names;
1764 char *t1_builtin_glyph_names[256];
1765 char charsetstr[0x4000];
1766 boolean read_encoding_only;
1770 #define T1_BUF_SIZE 0x10
1774 #define CS_VMOVETO 4
1775 #define CS_RLINETO 5
1776 #define CS_HLINETO 6
1777 #define CS_VLINETO 7
1778 #define CS_RRCURVETO 8
1779 #define CS_CLOSEPATH 9
1780 #define CS_CALLSUBR 10
1781 #define CS_RETURN 11
1782 #define CS_ESCAPE 12
1784 #define CS_ENDCHAR 14
1785 #define CS_RMOVETO 21
1786 #define CS_HMOVETO 22
1787 #define CS_VHCURVETO 30
1788 #define CS_HVCURVETO 31
1789 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1791 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1792 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1793 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1794 #define CS_SEAC CS_1BYTE_MAX + 6
1795 #define CS_SBW CS_1BYTE_MAX + 7
1796 #define CS_DIV CS_1BYTE_MAX + 12
1797 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1798 #define CS_POP CS_1BYTE_MAX + 17
1799 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1800 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1801 #define CS_MAX CS_2BYTE_MAX
1804 typedef unsigned char byte;
1806 byte nargs; /* number of arguments */
1807 boolean bottom; /* take arguments from bottom of stack? */
1808 boolean clear; /* clear stack? */
1810 } cc_entry; /* CharString Command */
1812 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1814 unsigned short len; /* length of the whole string */
1815 unsigned short cslen; /* length of the encoded part of the string */
1821 unsigned short t1_dr, t1_er;
1822 unsigned short t1_c1, t1_c2;
1823 unsigned short t1_cslen;
1826 @ @<Set initial...@>=
1827 mp->ps->t1_c1 = 52845;
1828 mp->ps->t1_c2 = 22719;
1831 typedef char t1_line_entry;
1832 typedef char t1_buf_entry;
1835 t1_line_entry *t1_line_ptr, *t1_line_array;
1836 size_t t1_line_limit;
1837 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1838 size_t t1_buf_limit;
1840 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1841 char *cs_dict_start, *cs_dict_end;
1842 int cs_count, cs_size, cs_size_pos;
1844 char *subr_array_start, *subr_array_end;
1845 int subr_max, subr_size, subr_size_pos;
1847 @ @<Set initial...@>=
1848 mp->ps->t1_line_array = NULL;
1849 mp->ps->t1_buf_array = NULL;
1852 This list contains the begin/end tokens commonly used in the
1853 /Subrs array of a Type 1 font.
1855 @<Static variables in the outer block@>=
1856 static const char *cs_token_pairs_list[][2] = {
1859 {" RD", "noaccess put"},
1860 {" -|", "noaccess put"},
1865 const char **cs_token_pair;
1866 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1867 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1868 long t1_block_length;
1876 @<Set initial ...@>=
1877 mp->ps->hexline_length = HEXLINE_WIDTH;
1880 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1881 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1882 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1883 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1884 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1885 @d t1_subrs() t1_prefix("/Subrs")
1886 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1887 @d t1_cleartomark() t1_prefix("cleartomark")
1889 @d isdigit(A) ((A)>='0'&&(A)<='9')
1892 static void end_hexline (MP mp) {
1893 if (mp->ps->hexline_length == HEXLINE_WIDTH) {
1895 mp->ps->hexline_length = 0;
1898 static void t1_check_pfa (MP mp) {
1899 const int c = t1_getchar (mp);
1900 mp->ps->t1_pfa = (c != 128) ? true : false;
1903 static int t1_getbyte (MP mp)
1905 int c = t1_getchar (mp);
1908 if (mp->ps->t1_block_length == 0) {
1910 mp_fatal_error (mp, "invalid marker");
1911 c = t1_getchar (mp);
1917 mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
1918 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 8;
1919 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 16;
1920 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 24;
1921 c = t1_getchar (mp);
1923 mp->ps->t1_block_length--;
1926 static int hexval (int c) {
1927 if (c >= 'A' && c <= 'F')
1928 return c - 'A' + 10;
1929 else if (c >= 'a' && c <= 'f')
1930 return c - 'a' + 10;
1931 else if (c >= '0' && c <= '9')
1936 static byte edecrypt (MP mp, byte cipher) {
1938 if (mp->ps->t1_pfa) {
1939 while (cipher == 10 || cipher == 13)
1940 cipher = t1_getbyte (mp);
1941 mp->ps->last_hexbyte = cipher = (hexval (cipher) << 4) + hexval (t1_getbyte (mp));
1943 plain = (cipher ^ (mp->ps->t1_dr >> 8));
1944 mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1947 static byte cdecrypt (MP mp, byte cipher, unsigned short *cr)
1949 const byte plain = (cipher ^ (*cr >> 8));
1950 *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1953 static byte eencrypt (MP mp, byte plain)
1955 const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1956 mp->ps->t1_er = (cipher + mp->ps->t1_er) * mp->ps->t1_c1 + mp->ps->t1_c2;
1960 static byte cencrypt (MP mp, byte plain, unsigned short *cr)
1962 const byte cipher = (plain ^ (*cr >> 8));
1963 *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1967 static char *eol (char *s) {
1968 char *p = strend (s);
1969 if (p - s > 1 && p[-1] != 10) {
1975 static float t1_scan_num (MP mp, char *p, char **r)
1980 if (sscanf (p, "%g", &f) != 1) {
1981 remove_eol (p, mp->ps->t1_line_array);
1982 snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1983 mp_fatal_error(mp,s);
1986 for (; isdigit (*p) || *p == '.' ||
1987 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1993 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1996 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1999 while (s1 >= begin_buf && s2 >= s) {
2008 @d alloc_array(T, n, s) do {
2009 if (mp->ps->T##_array == NULL) {
2010 mp->ps->T##_limit = (s);
2011 if ((unsigned)(n) > mp->ps->T##_limit)
2012 mp->ps->T##_limit = (n);
2013 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
2014 mp->ps->T##_ptr = mp->ps->T##_array;
2016 else if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
2017 size_t last_ptr_index;
2018 last_ptr_index = mp->ps->T##_ptr - mp->ps->T##_array;
2019 mp->ps->T##_limit *= 2;
2020 if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
2021 mp->ps->T##_limit = mp->ps->T##_ptr - mp->ps->T##_array + (n);
2022 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
2023 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
2027 @d out_eexec_char(A) t1_outhex(mp,(A))
2030 static void t1_outhex (MP mp, byte b)
2032 static char *hexdigits = "0123456789ABCDEF";
2033 t1_putchar (hexdigits[b / 16]);
2034 t1_putchar (hexdigits[b % 16]);
2035 mp->ps->hexline_length += 2;
2038 static void t1_getline (MP mp) {
2039 int c, l, eexec_scan;
2041 static const char eexec_str[] = "currentfile eexec";
2042 static int eexec_len = 17; /* |strlen(eexec_str)| */
2045 mp_fatal_error (mp,"unexpected end of file");
2046 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2047 alloc_array (t1_line, 1, T1_BUF_SIZE);
2048 mp->ps->t1_cslen = 0;
2050 c = t1_getbyte (mp);
2053 while (!t1_eof ()) {
2054 if (mp->ps->t1_in_eexec == 1)
2055 c = edecrypt (mp,c);
2056 alloc_array (t1_line, 1, T1_BUF_SIZE);
2057 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2058 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
2059 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
2064 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2066 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
2067 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2068 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2069 p = mp->ps->t1_line_ptr - 5;
2072 mp->ps->t1_cslen = l = t1_scan_num (mp, p + 1, 0);
2073 mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2074 /* |mp->ps->cs_start| is an index now */
2075 alloc_array (t1_line, l, T1_BUF_SIZE);
2077 *mp->ps->t1_line_ptr++ = edecrypt (mp,t1_getbyte (mp));
2079 c = t1_getbyte (mp);
2081 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
2082 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2083 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2085 if (eexec_scan == eexec_len)
2086 mp->ps->t1_in_eexec = 1;
2088 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2089 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2090 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2093 static void t1_putline (MP mp)
2095 char *p = mp->ps->t1_line_array;
2096 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2098 if (mp->ps->t1_eexec_encrypt) {
2099 while (p < mp->ps->t1_line_ptr)
2100 out_eexec_char (eencrypt (mp,*p++));
2102 while (p < mp->ps->t1_line_ptr)
2107 static void t1_puts (MP mp, const char *s)
2109 if (s != mp->ps->t1_line_array)
2110 strcpy (mp->ps->t1_line_array, s);
2111 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2115 static void t1_printf (MP mp, const char *fmt, ...)
2118 va_start (args, fmt);
2119 vsprintf (mp->ps->t1_line_array, fmt, args);
2120 t1_puts (mp,mp->ps->t1_line_array);
2124 static void t1_init_params (MP mp, char *open_name_prefix,
2125 char *cur_file_name) {
2126 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2127 t1_log (open_name_prefix);
2128 t1_log (cur_file_name);
2130 mp->ps->t1_lenIV = 4;
2131 mp->ps->t1_dr = 55665;
2132 mp->ps->t1_er = 55665;
2133 mp->ps->t1_in_eexec = 0;
2134 mp->ps->t1_cs = false;
2135 mp->ps->t1_scan = true;
2136 mp->ps->t1_synthetic = false;
2137 mp->ps->t1_eexec_encrypt = false;
2138 mp->ps->t1_block_length = 0;
2141 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2142 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2143 t1_log (close_name_suffix);
2148 static void t1_check_block_len (MP mp, boolean decrypt) {
2151 if (mp->ps->t1_block_length == 0)
2153 c = t1_getbyte (mp);
2155 c = edecrypt (mp,c);
2156 l = mp->ps->t1_block_length;
2157 if (!(l == 0 && (c == 10 || c == 13))) {
2158 snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2164 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2166 if (!mp->ps->t1_pfa)
2167 t1_check_block_len (mp, false);
2168 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2169 edecrypt (mp, t1_getbyte (mp));
2170 *mp->ps->t1_line_ptr++ = 0;
2172 mp->ps->t1_eexec_encrypt = true;
2173 if (!mp->ps->read_encoding_only)
2174 if (is_included (fm_cur))
2175 t1_putline (mp); /* to put the first four bytes */
2177 static void t1_stop_eexec (MP mp) {
2179 end_last_eexec_line ();
2180 if (!mp->ps->t1_pfa)
2181 t1_check_block_len (mp,true);
2183 c = edecrypt (mp, t1_getbyte (mp));
2184 if (!(c == 10 || c == 13)) {
2185 if (mp->ps->last_hexbyte == 0)
2188 mp_warn (mp,"unexpected data after eexec");
2191 mp->ps->t1_cs = false;
2192 mp->ps->t1_in_eexec = 2;
2194 static void t1_modify_fm (MP mp) {
2195 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2198 static void t1_modify_italic (MP mp) {
2199 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2204 const char *pdfname;
2214 static key_entry font_keys[FONT_KEYS_NUM] = {
2215 {"Ascent", "Ascender", 0, false},
2216 {"CapHeight", "CapHeight", 0, false},
2217 {"Descent", "Descender", 0, false},
2218 {"FontName", "FontName", 0, false},
2219 {"ItalicAngle", "ItalicAngle", 0, false},
2220 {"StemV", "StdVW", 0, false},
2221 {"XHeight", "XHeight", 0, false},
2222 {"FontBBox", "FontBBox", 0, false},
2234 @d ITALIC_ANGLE_CODE 4
2240 @d FONTBBOX4_CODE 10
2241 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2244 static void t1_scan_keys (MP mp, int tex_font,fm_entry *fm_cur) {
2248 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2249 if (t1_prefix ("/FontMatrix")) {
2253 if (t1_prefix ("/ItalicAngle")) {
2254 t1_modify_italic (mp);
2258 if (t1_prefix ("/FontType")) {
2259 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2260 if ((i = t1_scan_num (mp,p, 0)) != 1) {
2262 snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2263 mp_fatal_error(mp,s);
2267 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2268 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2270 if (key - font_keys == MAX_KEY_CODE)
2273 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2275 if ((k = key - font_keys) == FONTNAME_CODE) {
2278 remove_eol (p, mp->ps->t1_line_array);
2279 snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2280 mp_fatal_error(mp,s);
2282 r = ++p; /* skip the slash */
2283 if (is_included (fm_cur)) {
2284 /* save the fontname */
2285 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2286 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2287 mp->ps->fontname_buf[i]=0;
2289 if(is_subsetted (fm_cur)) {
2290 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2291 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2293 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2295 alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
2297 strncpy (r, fm_cur->subset_tag , 6);
2299 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2300 mp->ps->t1_line_ptr = eol (r);
2302 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2304 mp->ps->t1_line_ptr = eol (r);
2309 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2310 && (*p == '[' || *p == '{'))
2312 if (k == FONTBBOX1_CODE) {
2313 for (i = 0; i < 4; i++) {
2314 key[i].value = t1_scan_num (mp, p, &r);
2319 key->value = t1_scan_num (mp, p, 0);
2321 static void t1_scan_param (MP mp, int tex_font,fm_entry *fm_cur)
2323 static const char *lenIV = "/lenIV";
2324 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2326 if (t1_prefix (lenIV)) {
2327 mp->ps->t1_lenIV = t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2330 t1_scan_keys (mp, tex_font,fm_cur);
2332 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2333 if (glyph_names[b] != notdef) {
2334 mp_xfree (glyph_names[b]);
2335 glyph_names[b] = (char *) notdef;
2337 if (glyph_names[a] != notdef) {
2338 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2341 static void t1_builtin_enc (MP mp) {
2342 int i, a, b, c, counter = 0;
2345 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2347 if (t1_suffix ("def")) { /* predefined encoding */
2348 sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2349 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2350 for (i = 0; i < 256; i++)
2351 if (standard_glyph_names[i] == notdef)
2352 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2354 mp->ps->t1_builtin_glyph_names[i] =
2355 mp_xstrdup (mp,standard_glyph_names[i]);
2356 mp->ps->t1_encoding = ENC_STANDARD;
2359 snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2360 mp->ps->t1_buf_array);
2361 mp_fatal_error(mp,s);
2365 mp->ps->t1_encoding = ENC_BUILTIN;
2367 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2368 * not a predefined encoding
2370 * We have two possible forms of Encoding vector. The first case is
2372 * /Encoding [/a /b /c...] readonly def
2374 * and the second case can look like
2376 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2382 for (i = 0; i < 256; i++)
2383 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2384 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2385 r = strchr (mp->ps->t1_line_array, '[') + 1;
2389 for (p = mp->ps->t1_buf_array, r++;
2390 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2394 if (counter > 255) {
2396 (mp, "encoding vector contains more than 256 names");
2398 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2399 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2402 if (*r != 10 && *r != '%') {
2403 if (str_prefix (r, "] def")
2404 || str_prefix (r, "] readonly def"))
2408 remove_eol (r, mp->ps->t1_line_array);
2409 snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2410 mp->ps->t1_line_array);
2411 mp_fatal_error(mp,s);
2415 r = mp->ps->t1_line_array;
2417 } else { /* the second case */
2418 p = strchr (mp->ps->t1_line_array, 10);
2422 p = mp->ps->t1_line_array;
2425 check for `dup <index> <glyph> put'
2427 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2428 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2429 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2430 mp->ps->t1_builtin_glyph_names[i] =
2431 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2432 p = strstr (p, " put") + strlen (" put");
2436 check for `dup dup <to> exch <from> get put'
2438 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2439 && valid_code (a) && valid_code (b)) {
2440 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2441 p = strstr (p, " get put") + strlen (" get put");
2445 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2448 (p, "dup dup %i %i getinterval %i exch putinterval",
2449 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2450 && valid_code (c)) {
2451 for (i = 0; i < c; i++)
2452 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2453 p = strstr (p, " putinterval") + strlen (" putinterval");
2457 check for `def' or `readonly def'
2459 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2460 && strcmp (p, "def\n") == 0)
2463 skip an unrecognizable word
2466 while (*p != ' ' && *p != 10)
2474 static void t1_check_end (MP mp) {
2478 if (t1_prefix ("{restore}"))
2484 char *ff_name; /* base name of font file */
2485 char *ff_path; /* full path to font file */
2489 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2491 ff = check_ff_exist (mp, fm_cur);
2492 if (ff->ff_path != NULL) {
2493 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "rb", mp_filetype_font);
2495 mp_warn (mp, "cannot open Type 1 font file for reading");
2498 t1_init_params (mp,(char *)open_name_prefix,fm_cur->ff_name);
2499 mp->ps->fontfile_found = true;
2503 static void t1_scan_only (MP mp, int tex_font, fm_entry *fm_cur) {
2506 t1_scan_param (mp,tex_font, fm_cur);
2508 while (mp->ps->t1_in_eexec == 0);
2509 t1_start_eexec (mp,fm_cur);
2512 t1_scan_param (mp,tex_font, fm_cur);
2514 while (!(t1_charstrings () || t1_subrs ()));
2517 static void t1_include (MP mp, int tex_font, fm_entry *fm_cur) {
2520 t1_scan_param (mp,tex_font, fm_cur);
2523 while (mp->ps->t1_in_eexec == 0);
2524 t1_start_eexec (mp,fm_cur);
2527 t1_scan_param (mp,tex_font, fm_cur);
2530 while (!(t1_charstrings () || t1_subrs ()));
2531 mp->ps->t1_cs = true;
2536 while (!t1_end_eexec ());
2538 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2543 while (!t1_cleartomark ());
2544 t1_check_end (mp); /* write "{restore}if" if found */
2549 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2551 snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2552 mp_fatal_error(mp,s);
2556 static const char **check_cs_token_pair (MP mp) {
2557 const char **p = (const char **) cs_token_pairs_list;
2558 for (; p[0] != NULL; ++p)
2559 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2564 static void cs_store (MP mp, boolean is_subr) {
2568 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2569 *mp->ps->t1_buf_ptr++ = *p++);
2570 *mp->ps->t1_buf_ptr = 0;
2572 subr = t1_scan_num (mp, p + 1, 0);
2574 ptr = mp->ps->subr_tab + subr;
2576 ptr = mp->ps->cs_ptr++;
2577 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2579 snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2580 mp_fatal_error(mp,s);
2582 if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
2583 ptr->glyph_name = (char *) notdef;
2585 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2587 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2588 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2589 (unsigned) (mp->ps->t1_cslen + 4));
2590 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2591 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2592 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2593 *mp->ps->t1_buf_ptr++ = 10;
2594 if (is_subr && mp->ps->cs_token_pair == NULL)
2595 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2596 ptr->len = mp->ps->t1_buf_ptr - mp->ps->t1_buf_array;
2597 ptr->cslen = mp->ps->t1_cslen;
2598 ptr->data = mp_xmalloc (mp,ptr->len , sizeof (byte));
2599 memcpy (ptr->data, mp->ps->t1_buf_array, ptr->len);
2603 #define store_subr(mp) cs_store(mp,true)
2604 #define store_cs(mp) cs_store(mp,false)
2606 #define CC_STACK_SIZE 24
2608 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2609 static cc_entry cc_tab[CS_MAX];
2610 static boolean is_cc_init = false;
2614 if (stack_ptr - cc_stack < (N)) \
2618 #define stack_error(N) { \
2620 snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2621 (int) N, (int)(stack_ptr - cc_stack)); \
2627 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2629 #define cc_push(V) *stack_ptr++ = V
2630 #define cc_clear() stack_ptr = cc_stack
2632 #define set_cc(N, B, A, C) \
2633 cc_tab[N].nargs = A; \
2634 cc_tab[N].bottom = B; \
2635 cc_tab[N].clear = C; \
2636 cc_tab[N].valid = true
2638 static void cc_init (void) {
2642 for (i = 0; i < CS_MAX; i++)
2643 cc_tab[i].valid = false;
2644 set_cc (CS_HSTEM, true, 2, true);
2645 set_cc (CS_VSTEM, true, 2, true);
2646 set_cc (CS_VMOVETO, true, 1, true);
2647 set_cc (CS_RLINETO, true, 2, true);
2648 set_cc (CS_HLINETO, true, 1, true);
2649 set_cc (CS_VLINETO, true, 1, true);
2650 set_cc (CS_RRCURVETO, true, 6, true);
2651 set_cc (CS_CLOSEPATH, false, 0, true);
2652 set_cc (CS_CALLSUBR, false, 1, false);
2653 set_cc (CS_RETURN, false, 0, false);
2655 |set_cc(CS_ESCAPE, false, 0, false);|
2657 set_cc (CS_HSBW, true, 2, true);
2658 set_cc (CS_ENDCHAR, false, 0, true);
2659 set_cc (CS_RMOVETO, true, 2, true);
2660 set_cc (CS_HMOVETO, true, 1, true);
2661 set_cc (CS_VHCURVETO, true, 4, true);
2662 set_cc (CS_HVCURVETO, true, 4, true);
2663 set_cc (CS_DOTSECTION, false, 0, true);
2664 set_cc (CS_VSTEM3, true, 6, true);
2665 set_cc (CS_HSTEM3, true, 6, true);
2666 set_cc (CS_SEAC, true, 5, true);
2667 set_cc (CS_SBW, true, 4, true);
2668 set_cc (CS_DIV, false, 2, false);
2669 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2670 set_cc (CS_POP, false, 0, false);
2671 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2677 @d cs_getchar(mp) cdecrypt(mp,*data++, &cr)
2679 @d mark_subr(mp,n) cs_mark(mp,0, n)
2680 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2681 @d SMALL_BUF_SIZE 256
2684 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2685 char buf[SMALL_BUF_SIZE];
2688 va_start (args, fmt);
2689 vsprintf (buf, fmt, args);
2691 if (cs_name == NULL) {
2692 snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2694 snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2699 static void cs_mark (MP mp, const char *cs_name, int subr)
2705 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2709 if (cs_name == NULL) {
2711 ptr = mp->ps->subr_tab + subr;
2715 if (mp->ps->cs_notdef != NULL &&
2716 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2717 ptr = mp->ps->cs_notdef;
2719 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2720 if (strcmp (ptr->glyph_name, cs_name) == 0)
2722 if (ptr == mp->ps->cs_ptr) {
2724 snprintf (s,128,"glyph `%s' undefined", cs_name);
2728 if (ptr->glyph_name == notdef)
2729 mp->ps->cs_notdef = ptr;
2732 /* only marked CharString entries and invalid entries can be skipped;
2733 valid marked subrs must be parsed to keep the stack in sync */
2734 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2736 ptr->is_used = true;
2738 cs_len = ptr->cslen;
2739 data = ptr->data + 4;
2740 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2742 while (cs_len > 0) {
2744 b = cs_getchar (mp);
2748 else if (b <= 250) {
2750 a = ((b - 247) << 8) + 108 + cs_getchar (mp);
2751 } else if (b <= 254) {
2753 a = -((b - 251) << 8) - 108 - cs_getchar (mp);
2756 a = (cs_getchar (mp) & 0xff) << 24;
2757 a |= (cs_getchar (mp) & 0xff) << 16;
2758 a |= (cs_getchar (mp) & 0xff) << 8;
2759 a |= (cs_getchar (mp) & 0xff) << 0;
2760 if (sizeof (integer) > 4 && (a & 0x80000000))
2765 if (b == CS_ESCAPE) {
2766 b = cs_getchar (mp) + CS_1BYTE_MAX;
2770 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2776 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2780 if (stack_ptr - cc_stack < cc->nargs)
2781 cs_warn (mp,cs_name, subr,
2782 "less arguments on stack (%i) than required (%i)",
2783 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2784 else if (stack_ptr - cc_stack > cc->nargs)
2785 cs_warn (mp,cs_name, subr,
2786 "more arguments on stack (%i) than required (%i)",
2787 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2789 switch (cc - cc_tab) {
2794 if (!mp->ps->subr_tab[a1].valid) {
2795 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2803 case CS_CALLOTHERSUBR:
2804 if (cc_get (-1) == 3)
2805 lastargOtherSubr3 = cc_get (-3);
2806 a1 = cc_get (-2) + 2;
2810 cc_push (lastargOtherSubr3);
2811 /* the only case when we care about the value being pushed onto
2812 stack is when POP follows CALLOTHERSUBR (changing hints by
2820 mark_cs (mp,standard_glyph_names[a1]);
2821 mark_cs (mp,standard_glyph_names[a2]);
2830 cs_error: /* an error occured during parsing */
2833 ptr->is_used = false;
2836 static void t1_subset_ascii_part (MP mp, int tex_font, fm_entry *fm_cur)
2840 while (!t1_prefix ("/Encoding")) {
2841 t1_scan_param (mp,tex_font, fm_cur);
2845 t1_builtin_enc (mp);
2846 if (is_reencoded (fm_cur))
2847 mp->ps->t1_glyph_names = external_enc ();
2849 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2851 |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2852 make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2853 update_subset_tag ();
2856 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2857 t1_puts (mp,"/Encoding StandardEncoding def\n");
2860 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2861 for (i = 0, j = 0; i < 256; i++) {
2862 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2864 t1_printf (mp,"dup %i /%s put\n", (int) t1_char (i),
2865 mp->ps->t1_glyph_names[i]);
2868 /* We didn't mark anything for the Encoding array. */
2869 /* We add "dup 0 /.notdef put" for compatibility */
2870 /* with Acrobat 5.0. */
2872 t1_puts (mp,"dup 0 /.notdef put\n");
2873 t1_puts (mp,"readonly def\n");
2877 t1_scan_param (mp,tex_font, fm_cur);
2878 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
2881 while (mp->ps->t1_in_eexec == 0);
2884 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
2885 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
2887 static void cs_init (MP mp) {
2888 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2889 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2890 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2891 mp->ps->cs_token_pair = NULL;
2892 mp->ps->subr_tab = NULL;
2893 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2894 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2897 static void init_cs_entry ( cs_entry * cs) {
2899 cs->glyph_name = NULL;
2902 cs->is_used = false;
2906 static void t1_mark_glyphs (MP mp, int tex_font);
2908 static void t1_read_subrs (MP mp, int tex_font, fm_entry *fm_cur)
2913 while (!(t1_charstrings () || t1_subrs ())) {
2914 t1_scan_param (mp,tex_font, fm_cur);
2919 mp->ps->t1_cs = true;
2920 mp->ps->t1_scan = false;
2923 mp->ps->subr_size_pos = strlen ("/Subrs") + 1;
2924 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2925 mp->ps->subr_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2926 if (mp->ps->subr_size == 0) {
2927 while (!t1_charstrings ())
2931 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
2932 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,mp->ps->subr_size, sizeof (cs_entry));
2933 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2934 init_cs_entry (ptr);
2935 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2937 while (mp->ps->t1_cslen) {
2941 /* mark the first four entries without parsing */
2942 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2943 mp->ps->subr_tab[i].is_used = true;
2944 /* the end of the Subrs array might have more than one line so we need to
2945 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2946 the Subrs array followed by the CharStrings dict immediately (synthetic
2947 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2948 we will treat the font as synthetic and ignore everything until next
2951 #define POST_SUBRS_SCAN 5
2953 *mp->ps->t1_buf_array = 0;
2954 for (i = 0; i < POST_SUBRS_SCAN; i++) {
2955 if (t1_charstrings ())
2957 s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2958 alloc_array (t1_buf, s, T1_BUF_SIZE);
2959 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2962 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2963 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2964 suppose synthetic font */
2965 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2967 mp_xfree (ptr->data);
2968 mp_xfree (mp->ps->subr_tab);
2969 mp_xfree (mp->ps->subr_array_start);
2970 mp_xfree (mp->ps->subr_array_end);
2972 mp->ps->t1_cs = false;
2973 mp->ps->t1_synthetic = true;
2974 while (!(t1_charstrings () || t1_subrs ()))
2981 static void t1_flush_cs (MP mp, boolean is_subr)
2984 byte *r, *return_cs = NULL;
2985 cs_entry *tab, *end_tab, *ptr;
2986 char *start_line, *line_end;
2987 int count, size_pos;
2988 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2990 start_line = mp->ps->subr_array_start;
2991 line_end = mp->ps->subr_array_end;
2992 size_pos = mp->ps->subr_size_pos;
2993 tab = mp->ps->subr_tab;
2994 count = mp->ps->subr_max + 1;
2995 end_tab = mp->ps->subr_tab + count;
2997 start_line = mp->ps->cs_dict_start;
2998 line_end = mp->ps->cs_dict_end;
2999 size_pos = mp->ps->cs_size_pos;
3000 tab = mp->ps->cs_tab;
3001 end_tab = mp->ps->cs_ptr;
3002 count = mp->ps->cs_count;
3004 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
3005 for (p = start_line; p - start_line < size_pos;)
3006 *mp->ps->t1_line_ptr++ = *p++;
3007 while (isdigit (*p))
3009 sprintf (mp->ps->t1_line_ptr, "%u", count);
3010 strcat (mp->ps->t1_line_ptr, p);
3011 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3014 /* create |return_cs| to replace unsused subr's */
3018 return_cs = mp_xmalloc (mp, (mp->ps->t1_lenIV + 1) , sizeof(byte));
3019 if ( mp->ps->t1_lenIV > 0) {
3020 for (cs_len = 0, r = return_cs; cs_len < mp->ps->t1_lenIV; cs_len++, r++)
3021 *r = cencrypt (mp,0x00, &cr);
3022 *r = cencrypt (mp,CS_RETURN, &cr);
3024 *return_cs = CS_RETURN;
3029 for (ptr = tab; ptr < end_tab; ptr++) {
3032 sprintf (mp->ps->t1_line_array, "dup %i %u", (int) (ptr - tab),
3035 sprintf (mp->ps->t1_line_array, "/%s %u", ptr->glyph_name, ptr->cslen);
3036 p = strend (mp->ps->t1_line_array);
3037 memcpy (p, ptr->data, ptr->len);
3038 mp->ps->t1_line_ptr = p + ptr->len;
3041 /* replace unsused subr's by |return_cs| */
3043 sprintf (mp->ps->t1_line_array, "dup %i %u%s ", (int) (ptr - tab),
3044 cs_len, mp->ps->cs_token_pair[0]);
3045 p = strend (mp->ps->t1_line_array);
3046 memcpy (p, return_cs, cs_len);
3047 mp->ps->t1_line_ptr = p + cs_len;
3049 sprintf (mp->ps->t1_line_array, " %s", mp->ps->cs_token_pair[1]);
3050 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3054 mp_xfree (ptr->data);
3055 if (ptr->glyph_name != notdef)
3056 mp_xfree (ptr->glyph_name);
3058 sprintf (mp->ps->t1_line_array, "%s", line_end);
3059 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3062 mp_xfree (return_cs);
3064 mp_xfree (start_line);
3065 mp_xfree (line_end);
3068 static void t1_mark_glyphs (MP mp, int tex_font)
3071 char *charset = extra_charset ();
3074 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
3075 if (mp->ps->cs_tab != NULL)
3076 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3078 ptr->is_used = true;
3079 if (mp->ps->subr_tab != NULL) {
3080 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3082 ptr->is_used = true;
3083 mp->ps->subr_max = mp->ps->subr_size - 1;
3087 mark_cs (mp,notdef);
3088 for (i = 0; i < 256; i++)
3089 if (is_used_char (i)) {
3090 if (mp->ps->t1_glyph_names[i] == notdef) {
3092 snprintf(s,128, "character %i is mapped to %s", i, notdef);
3095 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3097 if (charset == NULL)
3099 g = s = charset + 1; /* skip the first '/' */
3102 while (*s != '/' && s < r)
3104 *s = 0; /* terminate g by rewriting '/' to 0 */
3109 if (mp->ps->subr_tab != NULL)
3110 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3111 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3113 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3114 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3117 static void t1_subset_charstrings (MP mp, int tex_font)
3120 mp->ps->cs_size_pos =
3121 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3122 - mp->ps->t1_line_array + 1;
3123 /* |cs_size_pos| points to the number indicating
3124 dict size after "/CharStrings" */
3125 mp->ps->cs_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3126 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,mp->ps->cs_size, sizeof(cs_entry));
3127 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3128 init_cs_entry (ptr);
3129 mp->ps->cs_notdef = NULL;
3130 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3132 while (mp->ps->t1_cslen) {
3136 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3137 t1_mark_glyphs (mp,tex_font);
3138 if (mp->ps->subr_tab != NULL) {
3139 if (mp->ps->cs_token_pair == NULL)
3141 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3144 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3150 static void t1_subset_end (MP mp)
3152 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3153 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3157 while (!t1_end_eexec ())
3158 t1_getline (mp); /* ignore the rest */
3159 t1_putline (mp); /* write "mark currentfile closefile" */
3161 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3166 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3167 while (!t1_cleartomark ()) {
3171 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3172 t1_check_end (mp); /* write "{restore}if" if found */
3176 static int t1_updatefm (MP mp, int f, fm_entry *fm)
3179 mp->ps->read_encoding_only = true;
3180 if (!t1_open_fontfile (mp,fm,NULL)) {
3183 t1_scan_only (mp,f, fm);
3184 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3186 while (*p != ' ' && *p != 0)
3190 t1_close_font_file (mp,"");
3195 static void writet1 (MP mp, int tex_font, fm_entry *fm_cur) {
3196 int save_selector = mp->selector;
3197 mp_normalize_selector(mp);
3198 mp->ps->read_encoding_only = false;
3199 if (!is_included (fm_cur)) { /* scan parameters from font file */
3200 if (!t1_open_fontfile (mp,fm_cur,"{"))
3202 t1_scan_only (mp,tex_font, fm_cur);
3203 t1_close_font_file (mp,"}");
3206 if (!is_subsetted (fm_cur)) { /* include entire font */
3207 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3209 t1_include (mp,tex_font,fm_cur);
3210 t1_close_font_file (mp,">>");
3213 /* partial downloading */
3214 if (!t1_open_fontfile (mp,fm_cur,"<"))
3216 t1_subset_ascii_part (mp,tex_font,fm_cur);
3217 t1_start_eexec (mp,fm_cur);
3220 t1_read_subrs (mp,tex_font, fm_cur);
3221 t1_subset_charstrings (mp,tex_font);
3223 t1_close_font_file (mp,">");
3224 mp->selector = save_selector;
3228 static void t1_free (MP mp);
3231 static void t1_free (MP mp) {
3232 mp_xfree (mp->ps->t1_line_array);
3233 mp_xfree (mp->ps->t1_buf_array);
3237 @* \[44d] Embedding fonts.
3239 @ The |tfm_num| is officially of type |font_number|, but that
3240 type does not exist yet at this point in the output order.
3244 char *tfm_name; /* TFM file name */
3245 char *ps_name; /* PostScript name */
3246 integer flags; /* font flags */
3247 char *ff_name; /* font file name */
3248 char *subset_tag; /* pseudoUniqueTag for subsetted font */
3249 enc_entry *encoding; /* pointer to corresponding encoding */
3250 unsigned int tfm_num; /* number of the TFM refering this entry */
3251 unsigned short type; /* font type (T1/TTF/...) */
3252 short slant; /* SlantFont */
3253 short extend; /* ExtendFont */
3254 integer ff_objnum; /* FontFile object number */
3255 integer fn_objnum; /* FontName/BaseName object number */
3256 integer fd_objnum; /* FontDescriptor object number */
3257 char *charset; /* string containing used glyphs */
3258 boolean all_glyphs; /* embed all glyphs? */
3259 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
3260 short tfm_avail; /* flags whether a tfm is available */
3261 short pid; /* Pid for truetype fonts */
3262 short eid; /* Eid for truetype fonts */
3268 #define FONTNAME_BUF_SIZE 128
3269 boolean fontfile_found;
3270 boolean is_otf_font;
3271 char fontname_buf[FONTNAME_BUF_SIZE];
3279 @d set_included(fm) ((fm)->type |= F_INCLUDED)
3280 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
3281 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
3282 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
3284 @d is_included(fm) ((fm)->type & F_INCLUDED)
3285 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
3286 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
3287 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
3288 @d is_reencoded(fm) ((fm)->encoding != NULL)
3289 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
3290 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
3292 @d fm_slant(fm) (fm)->slant
3293 @d fm_extend(fm) (fm)->extend
3294 @d fm_fontfile(fm) (fm)->ff_name
3297 static boolean mp_font_is_reencoded (MP mp, int f);
3298 static boolean mp_font_is_included (MP mp, int f);
3299 static boolean mp_font_is_subsetted (MP mp, int f);
3302 static boolean mp_font_is_reencoded (MP mp, int f) {
3304 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3306 && (fm->ps_name != NULL)
3307 && is_reencoded (fm))
3312 static boolean mp_font_is_included (MP mp, int f) {
3314 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3316 && (fm->ps_name != NULL && fm->ff_name != NULL)
3317 && is_included (fm))
3322 static boolean mp_font_is_subsetted (MP mp, int f) {
3324 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
3326 && (fm->ps_name != NULL && fm->ff_name != NULL)
3327 && is_included (fm) && is_subsetted (fm))
3333 @ @<Exported function headers@>=
3334 char * mp_fm_encoding_name (MP mp, int f);
3335 char * mp_fm_font_name (MP mp, int f);
3338 static char * mp_fm_font_subset_name (MP mp, int f);
3341 @c char * mp_fm_encoding_name (MP mp, int f) {
3344 if (mp_has_fm_entry (mp, f, &fm)) {
3345 if (fm != NULL && (fm->ps_name != NULL)) {
3346 if (is_reencoded (fm)) {
3348 if (e->enc_name!=NULL)
3349 return mp_xstrdup(mp,e->enc_name);
3355 print_err ("fontmap encoding problems for font ");
3356 mp_print(mp,mp->font_name[f]);
3360 char * mp_fm_font_name (MP mp, int f) {
3362 if (mp_has_fm_entry (mp, f,&fm)) {
3363 if (fm != NULL && (fm->ps_name != NULL)) {
3364 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3365 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3366 if (t1_updatefm(mp,f,fm)) {
3367 mp->font_ps_name_fixed[f] = true;
3369 print_err ("font loading problems for font ");
3370 mp_print(mp,mp->font_name[f]);
3374 return mp_xstrdup(mp,fm->ps_name);
3377 print_err ("fontmap name problems for font ");
3378 mp_print(mp,mp->font_name[f]);
3383 static char * mp_fm_font_subset_name (MP mp, int f) {
3385 if (mp_has_fm_entry (mp, f, &fm)) {
3386 if (fm != NULL && (fm->ps_name != NULL)) {
3387 if (is_subsetted(fm)) {
3388 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3389 snprintf(s,strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3392 return mp_xstrdup(mp,fm->ps_name);
3396 print_err ("fontmap name problems for font ");
3397 mp_print(mp,mp->font_name[f]);
3403 static integer mp_fm_font_slant (MP mp, int f);
3404 static integer mp_fm_font_extend (MP mp, int f);
3407 @c static integer mp_fm_font_slant (MP mp, int f) {
3409 if (mp_has_fm_entry (mp, f, &fm)) {
3410 if (fm != NULL && (fm->ps_name != NULL)) {
3416 static integer mp_fm_font_extend (MP mp, int f) {
3418 if (mp_has_fm_entry (mp, f, &fm)) {
3419 if (fm != NULL && (fm->ps_name != NULL)) {
3427 static boolean mp_do_ps_font (MP mp, font_number f);
3429 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3431 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3434 if (is_truetype(fm_cur) ||
3435 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3438 if (is_included(fm_cur)) {
3439 mp_ps_print_nl(mp,"%%BeginResource: font ");
3440 if (is_subsetted(fm_cur)) {
3441 mp_ps_print(mp, fm_cur->subset_tag);
3442 mp_ps_print_char(mp,'-');
3444 mp_ps_print(mp, fm_cur->ps_name);
3446 writet1 (mp,f,fm_cur);
3447 mp_ps_print_nl(mp,"%%EndResource");
3453 @ Included subset fonts do not need and encoding vector, make
3454 sure we skip that case.
3457 static void mp_list_used_resources (MP mp, int prologues, int procset);
3459 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
3460 font_number f; /* fonts used in a text node or as loop counters */
3461 int ff; /* a loop counter */
3462 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3465 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
3467 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3470 for (f=null_font+1;f<=mp->last_fnum;f++) {
3471 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3472 for (ff=ldf;ff>=null_font;ff--) {
3473 if ( mp_has_font_size(mp,ff) )
3474 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3477 if ( mp_font_is_subsetted(mp,f) )
3479 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
3480 (unsigned)mp->max_print_line )
3481 mp_ps_print_nl(mp, "%%+ encoding");
3484 mp_ps_print_nl(mp, "%%+ encoding");
3486 mp_ps_print_char(mp, ' ');
3487 mp_ps_print(mp, mp->font_enc_name[f]);
3495 for (f=null_font+1;f<=mp->last_fnum;f++) {
3496 if ( mp_has_font_size(mp,f) ) {
3497 for (ff=ldf;ff>=null_font;ff--) {
3498 if ( mp_has_font_size(mp,ff) )
3499 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3502 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
3503 (unsigned)mp->max_print_line )
3504 mp_ps_print_nl(mp, "%%+ font");
3507 mp_ps_print_nl(mp, "%%+ font");
3509 mp_ps_print_char(mp, ' ');
3510 if ( (prologues==3)&&
3511 (mp_font_is_subsetted(mp,f)) )
3512 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3514 mp_ps_print(mp, mp->font_ps_name[f]);
3524 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
3526 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
3527 font_number f; /* fonts used in a text node or as loop counters */
3528 int ff; /* a loop counter */
3529 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3532 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3534 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3537 for (f=null_font+1;f<=mp->last_fnum;f++) {
3538 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3539 for (ff=ldf;ff>= null_font;ff++) {
3540 if ( mp_has_font_size(mp,ff) )
3541 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3544 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3546 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(unsigned)mp->max_print_line )
3547 mp_ps_print_nl(mp, "%%+ encoding");
3550 mp_ps_print_nl(mp, "%%+ encoding");
3552 mp_ps_print_char(mp, ' ');
3553 mp_ps_print(mp, mp->font_enc_name[f]);
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 ( ! mp_font_is_included(mp,f) )
3571 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3572 mp_ps_print_nl(mp, "%%+ font");
3575 mp_ps_print_nl(mp, "%%+ font");
3577 mp_ps_print_char(mp, ' ');
3578 if ( mp_font_is_subsetted(mp,f) )
3579 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3581 mp_ps_print(mp, mp->font_ps_name[f]);
3591 @ @<Declarations...@>=
3592 static void mp_list_needed_resources (MP mp, int prologues);
3594 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
3595 font_number f; /* fonts used in a text node or as loop counters */
3596 int ff; /* a loop counter */
3597 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3601 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3602 if ( mp_has_font_size(mp,f)) {
3603 for (ff=ldf;ff>=null_font;ff--) {
3604 if ( mp_has_font_size(mp,ff) )
3605 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3608 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3610 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3611 mp_ps_print_nl(mp, "%%+ font");
3614 mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
3616 mp_ps_print_char(mp, ' ');
3617 mp_ps_print(mp, mp->font_ps_name[f]);
3623 if ( ! firstitem ) {
3627 for (f=null_font+1;f<= mp->last_fnum;f++) {
3628 if ( mp_has_font_size(mp,f) ) {
3629 for (ff=ldf;ff>=null_font;ff-- ) {
3630 if ( mp_has_font_size(mp,ff) )
3631 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3634 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3636 mp_ps_print(mp, "%%IncludeResource: font ");
3637 mp_ps_print(mp, mp->font_ps_name[f]);
3648 static void mp_write_font_definition (MP mp, font_number f, int prologues);
3652 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3653 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3655 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
3656 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3657 (mp_fm_font_extend(mp,f)!=0)||
3658 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3659 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3660 if ( (mp_font_is_subsetted(mp,f))&&
3661 (mp_font_is_included(mp,f))&&(prologues==3))
3662 mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3664 mp_ps_name_out(mp, mp->font_ps_name[f],true);
3665 mp_ps_print(mp, " fcp");
3667 if ( applied_reencoding(f) ) {
3668 mp_ps_print(mp, "/Encoding ");
3669 mp_ps_print(mp, mp->font_enc_name[f]);
3670 mp_ps_print(mp, " def ");
3672 if ( mp_fm_font_slant(mp,f)!=0 ) {
3673 mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
3674 mp_ps_print(mp, " SlantFont ");
3676 if ( mp_fm_font_extend(mp,f)!=0 ) {
3677 mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3678 mp_ps_print(mp, " ExtendFont ");
3680 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3681 mp_ps_print(mp, " 890 ScaleFont ");
3682 mp_ps_print(mp, " 277 SlantFont ");
3684 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3685 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3686 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3687 mp_ps_print(mp, "/space[0 -278]def ");
3688 mp_ps_print(mp, "/a12[-904 -939]def ");
3689 mp_ps_print(mp, "end def ");
3691 mp_ps_print(mp, "currentdict end");
3693 mp_ps_print_defined_name(mp,f,prologues);
3694 mp_ps_print(mp, " exch definefont pop");
3700 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
3703 @c static void mp_ps_print_defined_name(MP mp, font_number A, int prologues) {
3704 mp_ps_print(mp, " /");
3705 if ((mp_font_is_subsetted(mp,(A)))&&
3706 (mp_font_is_included(mp,(A)))&&(prologues==3))
3707 mp_ps_print(mp, mp_fm_font_subset_name(mp,(A)));
3709 mp_ps_print(mp, mp->font_ps_name[(A)]);
3710 if ( mp_xstrcmp(mp->font_name[(A)],"psyrgo")==0 )
3711 mp_ps_print(mp, "-Slanted");
3712 if ( mp_xstrcmp(mp->font_name[(A)],"zpzdr-reversed")==0 )
3713 mp_ps_print(mp, "-Reverse");
3714 if ( applied_reencoding((A)) ) {
3715 mp_ps_print(mp, "-");
3716 mp_ps_print(mp, mp->font_enc_name[(A)]);
3718 if ( mp_fm_font_slant(mp,(A))!=0 ) {
3719 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,(A))) ;
3721 if ( mp_fm_font_extend(mp,(A))!=0 ) {
3722 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,(A)));
3726 @ @<Include encodings and fonts for edge structure~|h|@>=
3727 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
3728 @<Embed fonts that are available@>
3730 @ @<Embed fonts that are available@>=
3733 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3736 for (f=null_font+1;f<=mp->last_fnum;f++) {
3737 if ( cur_fsize[f]!=null ) {
3738 if (prologues==3 ) {
3739 if ( ! mp_do_ps_font(mp,f) ) {
3740 if ( mp_has_fm_entry(mp,f, NULL) ) {
3741 print_err("Font embedding failed");
3746 cur_fsize[f]=link(cur_fsize[f]);
3747 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3751 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3753 } while (! done_fonts);
3756 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3759 mp_apply_mark_string_chars(mp, h, next_size);
3762 @ We also need to keep track of which characters are used in text nodes
3763 in the edge structure that is being shipped out. This is done by procedures
3764 that use the left-over |b3| field in the |char_info| words; i.e.,
3765 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3768 enum mp_char_mark_state {mp_unused=0, mp_used};
3771 void mp_mark_string_chars (MP mp,font_number f, char *s) ;
3774 void mp_mark_string_chars (MP mp,font_number f, char *s) {
3775 integer b; /* |char_base[f]| */
3776 ASCII_code bc,ec; /* only characters between these bounds are marked */
3777 char *k; /* an index into string |s| */
3783 if ( (*k>=bc)&&(*k<=ec) )
3784 mp->font_info[b+*k].qqqq.b3=mp_used;
3791 void mp_unmark_font (MP mp,font_number f) ;
3794 void mp_unmark_font (MP mp,font_number f) {
3795 int k; /* an index into |font_info| */
3796 for (k= mp->char_base[f]+mp->font_bc[f];
3797 k<=mp->char_base[f]+mp->font_ec[f];
3799 mp->font_info[k].qqqq.b3=mp_unused;
3804 void mp_print_improved_prologue (MP mp, struct mp_edge_object *h, int prologues, int procset) ;
3807 void mp_print_improved_prologue (MP mp, struct mp_edge_object *h, int prologues, int procset) {
3808 quarterword next_size; /* the size index for fonts being listed */
3809 pointer *cur_fsize; /* current positions in |font_sizes| */
3810 boolean done_fonts; /* have we finished listing the fonts in the header? */
3811 font_number f; /* a font number for loops */
3812 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3813 mp_list_used_resources(mp, prologues, procset);
3814 mp_list_supplied_resources(mp, prologues, procset);
3815 mp_list_needed_resources(mp, prologues);
3816 mp_ps_print_nl(mp, "%%EndComments");
3817 mp_ps_print_nl(mp, "%%BeginProlog");
3819 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
3821 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3822 mp_ps_print_nl(mp, "/bd{bind def}bind def"
3823 "/fshow {exch findfont exch scalefont setfont show}bd");
3824 if ( procset>0 ) @<Print the procset@>;
3825 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
3826 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3827 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3828 "/fmd{/FontMatrix exch def}bd");
3829 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3830 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3831 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3832 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3833 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3834 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3835 mp_ps_print_nl(mp, "%%EndResource");
3836 @<Include encodings and fonts for edge structure~|h|@>;
3837 mp_ps_print_nl(mp, "%%EndProlog");
3838 mp_ps_print_nl(mp, "%%BeginSetup");
3840 for (f=null_font+1;f<=mp->last_fnum;f++) {
3841 if ( mp_has_font_size(mp,f) ) {
3842 if ( mp_has_fm_entry(mp,f,NULL) ) {
3843 mp_write_font_definition(mp,f, prologues);
3844 mp_ps_name_out(mp, mp->font_name[f],true);
3845 mp_ps_print_defined_name(mp,f, prologues);
3846 mp_ps_print(mp, " def");
3849 snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3851 mp_ps_name_out(mp, mp->font_name[f],true);
3852 mp_ps_name_out(mp, mp->font_name[f],true);
3853 mp_ps_print(mp, " def");
3858 mp_ps_print_nl(mp, "%%EndSetup");
3859 mp_ps_print_nl(mp, "%%Page: 1 1");
3861 mp_xfree(cur_fsize);
3865 static font_number mp_print_font_comments (MP mp , struct mp_edge_object *h, int prologues);
3870 static font_number mp_print_font_comments (MP mp , struct mp_edge_object *h, int prologues) {
3871 quarterword next_size; /* the size index for fonts being listed */
3872 pointer *cur_fsize; /* current positions in |font_sizes| */
3873 int ff; /* a loop counter */
3874 boolean done_fonts; /* have we finished listing the fonts in the header? */
3875 font_number f; /* a font number for loops */
3876 scaled ds; /* design size and scale factor for a text node */
3877 font_number ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3878 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3879 if ( prologues>0 ) {
3880 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3881 |font_sizes| and eliminate duplicates@>;
3884 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3885 do { done_fonts=true;
3886 for (f=null_font+1;f<=mp->last_fnum;f++) {
3887 if ( cur_fsize[f]!=null ) {
3888 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3890 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
3892 if ( ! done_fonts ) {
3893 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3896 } while (! done_fonts);
3898 mp_xfree(cur_fsize);
3902 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3903 for (f=null_font+1;f<= mp->last_fnum;f++)
3904 cur_fsize[f]=mp->font_sizes[f]
3906 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3907 so we carefully remove duplicates. There is no harm in using a slow, brute-force
3910 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3913 for (f=null_font+1;f<= mp->last_fnum;f++) {
3914 if ( mp->font_sizes[f]!=null ) {
3915 if ( ldf==null_font )
3916 mp_ps_print_nl(mp, "%%DocumentFonts:");
3917 for (ff=ldf;ff>=null_font;ff--) {
3918 if ( mp->font_sizes[ff]!=null )
3919 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3922 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3923 mp_ps_print_nl(mp, "%%+");
3924 mp_ps_print_char(mp, ' ');
3925 mp_ps_print(mp, mp->font_ps_name[f]);
3934 static void mp_hex_digit_out (MP mp,small_number d) {
3935 if ( d<10 ) mp_ps_print_char(mp, d+'0');
3936 else mp_ps_print_char(mp, d+'a'-10);
3939 @ We output the marks as a hexadecimal bit string starting at |c| or
3940 |font_bc[f]|, whichever is greater. If the output has to be truncated
3941 to avoid exceeding |emergency_line_length| the return value says where to
3942 start scanning next time.
3945 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3948 @d emergency_line_length 255
3949 /* \ps\ output lines can be this long in unusual circumstances */
3952 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3953 eight_bits bc,ec; /* only encode characters between these bounds */
3954 integer lim; /* the maximum number of marks to encode before truncating */
3955 int p; /* |font_info| index for the current character */
3956 int d,b; /* used to construct a hexadecimal digit */
3957 lim=4*(emergency_line_length-mp->ps->ps_offset-4);
3961 @<Restrict the range |bc..ec| so that it contains no unused characters
3962 at either end and has length at most |lim|@>;
3963 @<Print the initial label indicating that the bitmap starts at |bc|@>;
3964 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3965 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3971 @ We could save time by setting the return value before the loop that
3972 decrements |ec|, but there is no point in being so tricky.
3974 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3975 p=mp->char_base[f]+bc;
3976 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3979 if ( ec>=bc+lim ) ec=bc+lim-1;
3980 p=mp->char_base[f]+ec;
3981 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3985 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3986 mp_ps_print_char(mp, ' ');
3987 mp_hex_digit_out(mp, bc / 16);
3988 mp_hex_digit_out(mp, bc % 16);
3989 mp_ps_print_char(mp, ':')
3993 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3995 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3997 mp_hex_digit_out(mp, d);
4000 if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
4003 mp_hex_digit_out(mp, d)
4006 @ Here is a simple function that determines whether there are any marked
4007 characters in font~|f| with character code at least~|c|.
4010 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
4013 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
4014 int p; /* |font_info| index for the current character */
4015 for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
4016 if ( mp->font_info[p].qqqq.b3==mp_used )
4023 @ If the file name is so long that it can't be printed without exceeding
4024 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
4025 line. We might have to repeat line in order to get the character usage
4026 information to fit within |emergency_line_length|.
4028 TODO: these two defines are also defined in mp.w!
4030 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
4031 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
4033 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
4035 while ( mp_check_ps_marks(mp, f,t) ) {
4036 mp_ps_print_nl(mp, "%*Font: ");
4037 if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
4039 mp_ps_print(mp, mp->font_name[f]);
4040 mp_ps_print_char(mp, ' ');
4041 ds=(mp->font_dsize[f] + 8) / 16;
4042 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
4043 if ( mp->ps->ps_offset+12>emergency_line_length ) break;
4044 mp_ps_print_char(mp, ' ');
4045 mp_ps_print_scaled(mp, ds);
4046 if ( mp->ps->ps_offset+5>emergency_line_length ) break;
4047 t=mp_ps_marks_out(mp, f,t);
4049 cur_fsize[f]=link(cur_fsize[f]);
4052 @ @<Print the procset@>=
4054 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
4055 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
4056 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
4057 "/p{closepath}bd/n{newpath}bd");
4058 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
4059 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
4060 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
4061 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
4062 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4066 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4067 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
4068 might not have reasonable |font_ps_name| entries, but we just charge ahead
4069 anyway. The user should not make \&{prologues} positive if this will cause
4071 @:prologues_}{\&{prologues} primitive@>
4074 void mp_print_prologue (MP mp, struct mp_edge_object *h, int prologues, int procset);
4077 void mp_print_prologue (MP mp, struct mp_edge_object *h, int prologues, int procset) {
4080 ldf = mp_print_font_comments (mp, h, prologues);
4082 if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
4083 mp_read_psname_table(mp);
4084 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4085 if ( (prologues>0)||(procset>0) ) {
4086 if ( ldf!=null_font ) {
4087 if ( prologues>0 ) {
4088 for (f=null_font+1;f<=mp->last_fnum;f++) {
4089 if ( mp_has_font_size(mp,f) ) {
4090 mp_ps_name_out(mp, mp->font_name[f],true);
4091 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4092 mp_ps_print(mp, " def");
4097 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4103 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4104 if ( (prologues>0)&&(ldf!=null_font) )
4106 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4108 mp_ps_print_nl(mp, "/bd{bind def}bind def");
4109 @<Print the procset@>;
4110 mp_ps_print_nl(mp, "%%EndResource");
4114 mp_ps_print(mp, "%%EndProlog");
4115 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4118 @ \MP\ used to have one single routine to print to both `write' files
4119 and the PostScript output. Web2c redefines ``Character |k| cannot be
4120 printed'', and that resulted in some bugs where 8-bit characters were
4121 written to the PostScript file (reported by Wlodek Bzyl).
4123 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4124 a plain space, since that makes it easier to parse the result file
4127 @<Character |k| is not allowed in PostScript output@>=
4130 @ We often need to print a pair of coordinates.
4133 void mp_ps_pair_out (MP mp,scaled x, scaled y) {
4135 mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
4136 mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
4140 void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4143 void mp_ps_print_cmd (MP mp, char *l, char *s) {
4144 if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4145 else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4149 void mp_ps_print_cmd (MP mp, char *l, char *s) ;
4152 void mp_ps_string_out (MP mp, char *s) {
4153 ASCII_code k; /* bits to be converted to octal */
4154 mp_ps_print(mp, "(");
4156 if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4157 mp_ps_print_char(mp, '\\');
4160 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4161 mp_ps_print_char(mp, '\\');
4162 mp_ps_print_char(mp, '0'+(k / 64));
4163 mp_ps_print_char(mp, '0'+((k / 8) % 8));
4164 mp_ps_print_char(mp, '0'+(k % 8));
4166 if ( (k=='(')||(k==')')||(k=='\\') )
4167 mp_ps_print_char(mp, '\\');
4168 mp_ps_print_char(mp, k);
4171 mp_ps_print_char(mp, ')');
4175 void mp_ps_string_out (MP mp, char *s) ;
4177 @ This is a define because the function does not use its |mp| argument.
4179 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4182 static boolean mp_do_is_ps_name (char *s) {
4183 ASCII_code k; /* the character being checked */
4185 if ( (k<=' ')||(k>'~') ) return false;
4186 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4187 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4193 void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4196 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4197 ps_room(strlen(s)+2);
4198 mp_ps_print_char(mp, ' ');
4199 if ( mp_is_ps_name(mp, s) ) {
4200 if ( lit ) mp_ps_print_char(mp, '/');
4203 mp_ps_string_out(mp, s);
4204 if ( ! lit ) mp_ps_print(mp, "cvx ");
4205 mp_ps_print(mp, "cvn");
4210 @ These special comments described in the {\sl PostScript Language Reference
4211 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4212 We can't normally output ``conforming'' \ps\ because
4213 the structuring conventions don't allow us to say ``Please make sure the
4214 following characters are downloaded and define the \.{fshow} macro to access
4217 The exact bounding box is written out if |mp_prologues<0|, although this
4218 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4219 accurately. (Overfull boxes are avoided if an illustration is made to
4220 match a given \.{\char`\\hsize}.)
4223 void mp_print_initial_comment(MP mp,struct mp_edge_object *hh, int prologues);
4226 void mp_print_initial_comment(MP mp,struct mp_edge_object *hh, int prologues) {
4228 mp_ps_print(mp, "%!PS");
4230 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
4231 mp_ps_print_nl(mp, "%%BoundingBox: ");
4232 if ( hh->_minx>hh->_maxx) {
4233 mp_ps_print(mp, "0 0 0 0");
4234 } else if ( prologues<0 ) {
4235 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4236 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4238 mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->_minx),mp_floor_scaled(mp, hh->_miny));
4239 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->_maxx),-mp_floor_scaled(mp, -hh->_maxy));
4241 mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
4242 if ( hh->_minx>hh->_maxx ) {
4243 mp_ps_print(mp, "0 0 0 0");
4245 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4246 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4248 mp_ps_print_nl(mp, "%%Creator: MetaPost ");
4249 mp_ps_print(mp, mp_metapost_version(mp));
4250 mp_ps_print_nl(mp, "%%CreationDate: ");
4251 mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
4252 mp_ps_print_char(mp, '.');
4253 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month]));
4254 mp_ps_print_char(mp, '.');
4255 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
4256 mp_ps_print_char(mp, ':');
4257 t=mp_round_unscaled(mp, mp->internal[mp_time]);
4258 mp_ps_print_dd(mp, t / 60);
4259 mp_ps_print_dd(mp, t % 60);
4260 mp_ps_print_nl(mp, "%%Pages: 1");
4263 @ The most important output procedure is the one that gives the \ps\ version of
4267 #define gr_left_type(A) (A)->left_type_field
4268 #define gr_right_type(A) (A)->right_type_field
4269 #define gr_x_coord(A) (A)->x_coord_field
4270 #define gr_y_coord(A) (A)->y_coord_field
4271 #define gr_left_x(A) (A)->left_x_field
4272 #define gr_left_y(A) (A)->left_y_field
4273 #define gr_right_x(A) (A)->right_x_field
4274 #define gr_right_y(A) (A)->right_y_field
4275 #define gr_next_knot(A) (A)->next_field
4276 #define gr_originator(A) (A)->originator_field
4277 typedef struct mp_knot {
4278 unsigned short left_type_field;
4279 unsigned short right_type_field;
4280 scaled x_coord_field;
4281 scaled y_coord_field;
4282 scaled left_x_field;
4283 scaled left_y_field;
4284 scaled right_x_field;
4285 scaled right_y_field;
4286 struct mp_knot * next_field;
4287 quarterword originator_field;
4291 struct mp_knot * mp_gr_insert_knot (MP mp, struct mp_knot *q, scaled x, scaled y) {
4292 /* returns the inserted knot */
4293 struct mp_knot *r; /* the new knot */
4294 r= mp_xmalloc(mp, 1, sizeof (struct mp_knot));
4295 gr_next_knot(r)=gr_next_knot(q); gr_next_knot(q)=r;
4296 gr_right_x(r)=gr_right_x(q);
4297 gr_right_y(r)=gr_right_y(q);
4300 gr_right_x(q)=gr_x_coord(q);
4301 gr_right_y(q)=gr_y_coord(q);
4302 gr_left_x(r)=gr_x_coord(r);
4303 gr_left_y(r)=gr_y_coord(r);
4304 gr_left_type(r)=mp_explicit;
4305 gr_right_type(r)=mp_explicit;
4306 gr_originator(r)=mp_program_code;
4311 @ If we want to duplicate a knot node, we can say |copy_knot|:
4314 struct mp_knot *mp_gr_copy_knot (MP mp, struct mp_knot *p) {
4315 struct mp_knot *q; /* the copy */
4316 q = mp_xmalloc(mp, 1, sizeof (struct mp_knot));
4317 memcpy(q,p,sizeof (struct mp_knot));
4318 gr_next_knot(q)=NULL;
4322 @ The |copy_path| routine makes a clone of a given path.
4325 struct mp_knot *mp_gr_copy_path (MP mp, struct mp_knot *p) {
4326 struct mp_knot *q, *pp, *qq; /* for list manipulation */
4327 q=mp_gr_copy_knot(mp, p);
4331 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4332 qq=gr_next_knot(qq);
4333 pp=gr_next_knot(pp);
4339 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4340 returns a pointer to the first node of the copy, if the path is a cycle,
4341 but to the final node of a non-cyclic copy. The global
4342 variable |path_tail| will point to the final node of the original path;
4343 this trick makes it easier to implement `\&{doublepath}'.
4345 All node types are assumed to be |endpoint| or |explicit| only.
4348 struct mp_knot * mp_gr_htap_ypoc (MP mp, struct mp_knot *p) {
4349 struct mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4350 q=mp_xmalloc(mp, 1, sizeof (struct mp_knot)); /* this will correspond to |p| */
4353 gr_right_type(qq)=gr_left_type(pp);
4354 gr_left_type(qq)=gr_right_type(pp);
4355 gr_x_coord(qq)=gr_x_coord(pp);
4356 gr_y_coord(qq)=gr_y_coord(pp);
4357 gr_right_x(qq)=gr_left_x(pp);
4358 gr_right_y(qq)=gr_left_y(pp);
4359 gr_left_x(qq)=gr_right_x(pp);
4360 gr_left_y(qq)=gr_right_y(pp);
4361 gr_originator(qq)=gr_originator(pp);
4362 if ( gr_next_knot(pp)==p ) {
4364 /* mp->path_tail=pp; */ /* ? */
4367 rr=mp_xmalloc(mp, 1, sizeof (struct mp_knot));
4368 gr_next_knot(rr)=qq;
4370 pp=gr_next_knot(pp);
4374 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4375 calling the following subroutine.
4378 void mp_do_gr_toss_knot_list (struct mp_knot *p) ;
4381 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4384 void mp_do_gr_toss_knot_list (struct mp_knot * p) {
4385 struct mp_knot *q; /* the node being freed */
4386 struct mp_knot *r; /* the next node */
4399 void mp_gr_ps_path_out (MP mp, struct mp_knot *h) {
4400 struct mp_knot *p, *q; /* for scanning the path */
4401 scaled d; /* a temporary value */
4402 boolean curved; /* |true| unless the cubic is almost straight */
4404 mp_ps_print_cmd(mp, "newpath ","n ");
4405 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4406 mp_ps_print_cmd(mp, "moveto","m");
4409 if ( gr_right_type(p)==mp_endpoint ) {
4410 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4414 @<Start a new line and print the \ps\ commands for the curve from
4418 mp_ps_print_cmd(mp, " closepath"," p");
4421 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4423 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4426 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4427 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4428 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4429 mp_ps_print_cmd(mp, "curveto","c");
4431 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4432 mp_ps_print_cmd(mp, "lineto","l");
4435 @ Two types of straight lines come up often in \MP\ paths:
4436 cubics with zero initial and final velocity as created by |make_path| or
4437 |make_envelope|, and cubics with control points uniformly spaced on a line
4438 as created by |make_choices|.
4440 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4442 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4443 if ( gr_right_x(p)==gr_x_coord(p) )
4444 if ( gr_right_y(p)==gr_y_coord(p) )
4445 if ( gr_left_x(q)==gr_x_coord(q) )
4446 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4447 d=gr_left_x(q)-gr_right_x(p);
4448 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4449 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4450 { d=gr_left_y(q)-gr_right_y(p);
4451 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4452 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4455 @ The colored objects use a union to express the color parts:
4460 scaled _red_val, _green_val, _blue_val;
4463 scaled _cyan_val, _magenta_val, _yellow_val, _black_val;
4472 @d gr_start_x(A) (A)->start_x_field
4473 @d gr_stop_x(A) (A)->stop_x_field
4474 @d gr_dash_link(A) (A)->next_field
4476 @d gr_dash_list(A) (A)->list_field
4477 @d gr_dash_y(A) (A)->y_field
4480 typedef struct mp_dash_item {
4481 scaled start_x_field;
4482 scaled stop_x_field;
4483 struct mp_dash_item *next_field;
4485 typedef struct mp_dash_list {
4486 struct mp_dash_item *list_field;
4492 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
4495 void mp_do_gr_toss_dashes(struct mp_dash_list *dl);
4498 void mp_do_gr_toss_dashes(struct mp_dash_list *dl) {
4499 struct mp_dash_item *di, *dn;
4500 di = gr_dash_list(dl);
4502 dn = gr_dash_link(di);
4510 @ Now for outputting the actual graphic objects. First, set up some
4511 structures and access macros.
4513 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4516 #define gr_type(A) (A)->_type_field
4517 #define gr_link(A) (A)->_link_field
4518 #define gr_name_type(A) (A)->name_type_field
4519 #define gr_color_model(A) (A)->color_model_field
4520 #define gr_red_val(A) (A)->color_field.rgb._red_val
4521 #define gr_green_val(A) (A)->color_field.rgb._green_val
4522 #define gr_blue_val(A) (A)->color_field.rgb._blue_val
4523 #define gr_cyan_val(A) (A)->color_field.cmyk._cyan_val
4524 #define gr_magenta_val(A) (A)->color_field.cmyk._magenta_val
4525 #define gr_yellow_val(A) (A)->color_field.cmyk._yellow_val
4526 #define gr_black_val(A) (A)->color_field.cmyk._black_val
4527 #define gr_grey_val(A) (A)->color_field.grey._grey_val
4528 #define gr_path_p(A) (A)->path_p_field
4529 #define gr_htap_p(A) (A)->htap_p_field
4530 #define gr_pen_p(A) (A)->pen_p_field
4531 #define gr_ljoin_val(A) (A)->ljoin_field
4532 #define gr_lcap_val(A) (A)->lcap_field
4533 #define gr_dash_scale(A) (A)->dash_scale_field
4534 #define gr_miterlim_val(A) (A)->miterlim_field
4535 #define gr_pre_script(A) (A)->pre_script_field
4536 #define gr_post_script(A) (A)->post_script_field
4537 #define gr_dash_p(A) (A)->dash_p_field
4538 #define gr_text_p(A) (A)->text_p_field
4539 #define gr_font_n(A) (A)->font_n_field
4540 #define gr_font_name(A) (A)->font_name_field
4541 #define gr_font_dsize(A) (A)->font_dsize_field
4542 #define gr_width_val(A) (A)->width_field
4543 #define gr_height_val(A) (A)->height_field
4544 #define gr_depth_val(A) (A)->depth_field
4545 #define gr_tx_val(A) (A)->tx_field
4546 #define gr_ty_val(A) (A)->ty_field
4547 #define gr_txx_val(A) (A)->txx_field
4548 #define gr_txy_val(A) (A)->txy_field
4549 #define gr_tyx_val(A) (A)->tyx_field
4550 #define gr_tyy_val(A) (A)->tyy_field
4551 typedef struct mp_graphic_object {
4552 halfword _type_field;
4553 quarterword name_type_field;
4554 struct mp_graphic_object * _link_field;
4555 struct mp_knot * path_p_field;
4556 struct mp_knot * htap_p_field;
4557 struct mp_knot * pen_p_field;
4558 quarterword color_model_field;
4559 mp_color color_field;
4560 quarterword ljoin_field ;
4561 quarterword lcap_field ;
4562 scaled miterlim_field ;
4563 scaled dash_scale_field ;
4564 char *pre_script_field;
4565 char *post_script_field;
4566 struct mp_dash_list *dash_p_field;
4568 font_number font_n_field ;
4569 char *font_name_field ;
4570 scaled font_dsize_field ;
4571 scaled width_field ;
4572 scaled height_field ;
4573 scaled depth_field ;
4580 } mp_graphic_object;
4581 typedef struct mp_edge_object {
4582 struct mp_graphic_object * body;
4583 struct mp_edge_object * _next;
4586 scaled _minx, _miny, _maxx, _maxy;
4589 @ @<Exported function headers@>=
4590 struct mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4593 struct mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4594 mp_graphic_object *p;
4595 p = mp_xmalloc(mp,1,sizeof(struct mp_graphic_object));
4596 memset(p,0,sizeof(struct mp_graphic_object));
4601 @ We need to keep track of several parameters from the \ps\ graphics state.
4603 This allows us to be sure that \ps\ has the correct values when they are
4604 needed without wasting time and space setting them unnecessarily.
4606 @d gs_red mp->ps->gs_state->red_field
4607 @d gs_green mp->ps->gs_state->green_field
4608 @d gs_blue mp->ps->gs_state->blue_field
4609 @d gs_black mp->ps->gs_state->black_field
4610 @d gs_colormodel mp->ps->gs_state->colormodel_field
4611 @d gs_ljoin mp->ps->gs_state->ljoin_field
4612 @d gs_lcap mp->ps->gs_state->lcap_field
4613 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4614 @d gs_miterlim mp->ps->gs_state->miterlim_field
4615 @d gs_dash_p mp->ps->gs_state->dash_p_field
4616 @d gs_previous mp->ps->gs_state->previous_field
4617 @d gs_dash_sc mp->ps->gs_state->dash_sc_field
4618 @d gs_width mp->ps->gs_state->width_field
4621 typedef struct _gs_state {
4623 scaled green_field ;
4625 scaled black_field ;
4626 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4627 quarterword colormodel_field ;
4628 /* the current colormodel */
4629 quarterword ljoin_field ;
4630 quarterword lcap_field ;
4631 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4632 quarterword adj_wx_field ;
4633 /* what resolution-dependent adjustment applies to the width */
4634 scaled miterlim_field ;
4635 /* the value from the last \&{setmiterlimit} command */
4636 struct mp_dash_list * dash_p_field ;
4637 /* edge structure for last \&{setdash} command */
4638 struct _gs_state * previous_field ;
4639 /* backlink to the previous |_gs_state| structure */
4640 scaled dash_sc_field ;
4641 /* scale factor used with |gs_dash_p| */
4642 scaled width_field ;
4643 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4648 struct _gs_state * gs_state;
4651 mp->ps->gs_state=NULL;
4653 @ @<Dealloc variables@>=
4654 mp_xfree(mp->ps->gs_state);
4656 @ To avoid making undue assumptions about the initial graphics state, these
4657 parameters are given special values that are guaranteed not to match anything
4658 in the edge structure being shipped out. On the other hand, the initial color
4659 should be black so that the translation of an all-black picture will have no
4660 \&{setcolor} commands. (These would be undesirable in a font application.)
4661 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4662 to recover from a situation where we have lost track of the graphics state.
4665 void mp_gs_unknown_graphics_state (MP mp,scaled c) ;
4668 @d mp_void (null+1) /* a null pointer different from |null| */
4670 @c void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4671 struct _gs_state *p; /* to shift graphic states around */
4672 if ( (c==0)||(c==-1) ) {
4673 if ( mp->ps->gs_state==NULL ) {
4674 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4677 while ( gs_previous!=NULL ) {
4679 mp_xfree(mp->ps->gs_state);
4683 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4684 gs_colormodel=mp_uninitialized_model;
4691 } else if ( c==1 ) {
4692 p= mp->ps->gs_state;
4693 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4694 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4696 } else if ( c==2 ) {
4698 mp_xfree(mp->ps->gs_state);
4704 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4705 that \ps's idea of the graphics state agrees with what is stored in the object.
4708 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) ;
4711 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) {
4712 /* get ready to output graphical object |p| */
4713 struct mp_knot *pp; /* for list manipulation */
4714 struct mp_dash_list *hh;
4715 scaled wx,wy,ww; /* dimensions of pen bounding box */
4716 boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4717 integer tx,ty; /* temporaries for computing |adj_wx| */
4718 scaled scf; /* a scale factor for the dash pattern */
4719 if ( gr_has_color(p) )
4720 @<Make sure \ps\ will use the right color for object~|p|@>;
4721 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) )
4722 if ( gr_pen_p(p)!=NULL )
4723 if ( pen_is_elliptical(gr_pen_p(p)) ) {
4724 @<Generate \ps\ code that sets the stroke width to the
4725 appropriate rounded value@>;
4726 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4727 @<Decide whether the line cap parameter matters and set it if necessary@>;
4728 @<Set the other numeric parameters as needed for object~|p|@>;
4730 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
4733 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4734 if ( gr_type(p)==mp_stroked_code )
4735 if ( (gr_left_type(gr_path_p(p))==mp_endpoint)||(gr_dash_p(p)!=NULL) )
4736 if ( gs_lcap!=gr_lcap_val(p) ) {
4738 mp_ps_print_char(mp, ' ');
4739 mp_ps_print_char(mp, '0'+gr_lcap_val(p));
4740 mp_ps_print_cmd(mp, " setlinecap"," lc");
4741 gs_lcap=gr_lcap_val(p);
4744 @ @<Set the other numeric parameters as needed for object~|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 @ @<Make sure \ps\ will use the right color for object~|p|@>=
4762 if ( (gr_color_model(p)==mp_rgb_model)||
4763 ((gr_color_model(p)==mp_uninitialized_model)&&
4764 ((mp->internal[mp_default_color_model]>>16)==mp_rgb_model)) ) {
4765 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=gr_red_val(p))||
4766 (gs_green!=gr_green_val(p))||(gs_blue!=gr_blue_val(p)) ) {
4767 gs_red=gr_red_val(p);
4768 gs_green=gr_green_val(p);
4769 gs_blue=gr_blue_val(p);
4771 gs_colormodel=mp_rgb_model;
4773 mp_ps_print_char(mp, ' ');
4774 mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
4775 mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
4776 mp_ps_print_scaled(mp, gs_blue);
4777 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4780 } else if ( (gr_color_model(p)==mp_cmyk_model)||
4781 ((gr_color_model(p)==mp_uninitialized_model)&&
4782 ((mp->internal[mp_default_color_model]>>16)==mp_cmyk_model)) ) {
4783 if ( (gs_red!=gr_cyan_val(p))||(gs_green!=gr_magenta_val(p))||
4784 (gs_blue!=gr_yellow_val(p))||(gs_black!=gr_black_val(p))||
4785 (gs_colormodel!=mp_cmyk_model) ) {
4786 if ( gr_color_model(p)==mp_uninitialized_model ) {
4792 gs_red=gr_cyan_val(p);
4793 gs_green=gr_magenta_val(p);
4794 gs_blue=gr_yellow_val(p);
4795 gs_black=gr_black_val(p);
4797 gs_colormodel=mp_cmyk_model;
4799 mp_ps_print_char(mp, ' ');
4800 mp_ps_print_scaled(mp, gs_red);
4801 mp_ps_print_char(mp, ' ');
4802 mp_ps_print_scaled(mp, gs_green);
4803 mp_ps_print_char(mp, ' ');
4804 mp_ps_print_scaled(mp, gs_blue);
4805 mp_ps_print_char(mp, ' ');
4806 mp_ps_print_scaled(mp, gs_black);
4807 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4810 } else if ( (gr_color_model(p)==mp_grey_model)||
4811 ((gr_color_model(p)==mp_uninitialized_model)&&
4812 ((mp->internal[mp_default_color_model]>>16)==mp_grey_model)) ) {
4813 if ( (gs_red!=gr_grey_val(p))||(gs_colormodel!=mp_grey_model) ) {
4814 gs_red = gr_grey_val(p);
4818 gs_colormodel=mp_grey_model;
4820 mp_ps_print_char(mp, ' ');
4821 mp_ps_print_scaled(mp, gs_red);
4822 mp_ps_print_cmd(mp, " setgray"," G");
4826 if ( gr_color_model(p)==mp_no_model )
4827 gs_colormodel=mp_no_model;
4830 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4831 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4832 @:setwidth}{\&{setwidth}command@>
4833 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4834 generate \ps\ code that computes the rounded value. For non-circular pens, the
4835 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4836 have the computed width.
4838 Rounding the width to whole pixels is not likely to improve the appearance of
4839 diagonal or curved strokes, but we do it anyway for consistency. The
4840 \&{truncate} command generated here tends to make all the strokes a little
4841 @:truncate}{\&{truncate} command@>
4842 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4843 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4844 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4847 @<Generate \ps\ code that sets the stroke width...@>=
4848 @<Set |wx| and |wy| to the width and height of the bounding box for
4850 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4851 important and set |adj_wx| and |ww| accordingly@>;
4852 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4855 mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
4857 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4859 if ( mp->internal[mp_procset]>0 ) {
4861 mp_ps_print_char(mp, ' ');
4862 mp_ps_print_scaled(mp, ww);
4863 mp_ps_print(mp, " vlw");
4866 mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
4867 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4874 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4876 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4877 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4878 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4880 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4881 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4884 @ The path is considered ``essentially horizontal'' if its range of
4885 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4886 vertical'' paths are detected similarly. This code ensures that no component
4887 of the pen transformation is more that |aspect_bound*(ww+1)|.
4889 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4890 more than this factor */
4895 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4897 if ( mp_gr_coord_rangeOK(gr_path_p(p), do_y_loc, wy) ) tx=aspect_bound;
4898 else if ( mp_gr_coord_rangeOK(gr_path_p(p), do_x_loc, wx) ) ty=aspect_bound;
4899 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
4900 else { ww=wx; adj_wx=true; }
4902 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4903 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4904 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4905 bounding-box computation.
4908 boolean mp_gr_coord_rangeOK (struct mp_knot *h,
4909 small_number zoff, scaled dz);
4912 boolean mp_gr_coord_rangeOK (struct mp_knot *h,
4913 small_number zoff, scaled dz) {
4914 struct mp_knot *p; /* for scanning the path form |h| */
4915 scaled zlo,zhi; /* coordinate range so far */
4916 scaled z; /* coordinate currently being tested */
4917 if (zoff==do_x_loc) {
4921 while ( gr_right_type(p)!=mp_endpoint ) {
4923 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4924 p=gr_next_knot(p); z=gr_left_x(p);
4925 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4927 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4934 while ( gr_right_type(p)!=mp_endpoint ) {
4936 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4937 p=gr_next_knot(p); z=gr_left_y(p);
4938 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4940 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4947 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4949 else if ( z>zhi ) zhi=z;
4950 if ( zhi-zlo>dz ) return false
4952 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4953 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4954 @:stroke}{\&{stroke} command@>
4955 @:fill}{\&{fill} command@>
4956 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4959 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4960 if ( gr_type(p)==mp_fill_code ) {
4964 scf=mp_gr_get_pen_scale(mp, gr_pen_p(p));
4966 if ( gs_width==0 ) scf=gr_dash_scale(p); else hh=NULL;
4968 scf=mp_make_scaled(mp, gs_width,scf);
4969 scf=mp_take_scaled(mp, scf,gr_dash_scale(p));
4973 if ( gs_dash_p!=NULL ) {
4974 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4977 } else if ( (gs_dash_sc!=scf) || ! mp_gr_same_dashes(gs_dash_p,hh) ) {
4978 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
4982 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) ;
4986 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) {
4987 return mp_sqrt_det(mp,
4988 gr_left_x(p)-gr_x_coord(p), gr_right_x(p)-gr_x_coord(p),
4989 gr_left_y(p)-gr_y_coord(p), gr_right_y(p)-gr_y_coord(p));
4993 @ Translating a dash list into \ps\ is very similar to printing it symbolically
4994 in |print_edges|. A dash pattern with |dash_y(hh)=0| has length zero and is
4995 ignored. The same fate applies in the bizarre case of a dash pattern that
4996 cannot be printed without overflow.
4998 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5001 if ( (gr_dash_p(p)==NULL) ||
5002 (gr_dash_y(hh)==0) ||
5003 ((abs(gr_dash_y(hh)) / unity) >= (el_gordo / scf))) {
5004 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5006 struct mp_dash_item *dpp=gr_dash_list(hh);
5007 struct mp_dash_item *pp= dpp;
5009 mp_ps_print(mp, " [");
5010 while ( dpp!=NULL ) {
5012 dx = mp_take_scaled(mp, gr_stop_x(dpp)-gr_start_x(dpp),scf);
5014 if (gr_dash_link(dpp)!=NULL) {
5015 dy = mp_take_scaled(mp, gr_start_x(gr_dash_link(dpp))-gr_stop_x(dpp),scf);
5017 dy = mp_take_scaled(mp, (gr_start_x(pp)+gr_dash_y(hh))-gr_stop_x(dpp),scf);
5019 mp_ps_pair_out(mp, dx, dy);
5020 dpp=gr_dash_link(dpp);
5023 mp_ps_print(mp, "] ");
5024 mp_ps_print_scaled(mp, mp_take_scaled(mp, mp_gr_dash_offset(mp, hh),scf));
5025 mp_ps_print_cmd(mp, " setdash"," sd");
5030 boolean mp_gr_same_dashes (struct mp_dash_list *h, struct mp_dash_list *hh) ;
5033 boolean mp_gr_same_dashes (struct mp_dash_list * h, struct mp_dash_list *hh) {
5034 /* do |h| and |hh| represent the same dash pattern? */
5035 struct mp_dash_item * p, *pp; /* dash nodes being compared */
5036 if ( h==hh ) return true;
5037 else if ( (h==NULL)||(hh==NULL) ) return false;
5038 else if ( gr_dash_y(h)!=gr_dash_y(hh) ) return false;
5039 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5040 return false; /* can't happen */
5043 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5044 { p=gr_dash_list(h);
5045 pp=gr_dash_list(hh);
5046 while ( (p!=NULL)&&(pp!=NULL) ) {
5047 if ( (gr_start_x(p)!=gr_start_x(pp))||
5048 (gr_stop_x(p)!=gr_stop_x(pp)) ) {
5052 pp=gr_dash_link(pp);
5059 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) ;
5062 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) {
5063 scaled x; /* the answer */
5064 if ( h==NULL || (gr_dash_list(h)==NULL) || (gr_dash_y(h)<0) )
5065 mp_confusion(mp, "dash0");
5066 @:this can't happen dash0}{\quad dash0@>
5067 if ( gr_dash_y(h)==0 ) {
5070 x=-(gr_start_x(gr_dash_list(h)) % gr_dash_y(h));
5071 if ( x<0 ) x=x+gr_dash_y(h);
5076 @ When stroking a path with an elliptical pen, it is necessary to transform
5077 the coordinate system so that a unit circular pen will have the desired shape.
5078 To keep this transformation local, we enclose it in a
5079 $$\&{gsave}\ldots\&{grestore}$$
5080 block. Any translation component must be applied to the path being stroked
5081 while the rest of the transformation must apply only to the pen.
5082 If |fill_also=true|, the path is to be filled as well as stroked so we must
5083 insert commands to do this after giving the path.
5086 void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) ;
5089 @c void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) {
5090 /* generate an elliptical pen stroke from object |h| */
5091 scaled txx,txy,tyx,tyy; /* transformation parameters */
5092 struct mp_knot *p; /* the pen to stroke with */
5093 scaled d1,det; /* for tweaking transformation parameters */
5094 integer s; /* also for tweaking transformation paramters */
5095 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5097 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5099 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5100 mp_gr_ps_path_out(mp, gr_path_p(h));
5101 if ( mp->internal[mp_procset]==0 ) {
5102 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5103 @<Issue \ps\ commands to transform the coordinate system@>;
5104 mp_ps_print(mp, " stroke");
5105 if ( transformed ) mp_ps_print(mp, " grestore");
5107 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5108 if ( (txy!=0)||(tyx!=0) ) {
5109 mp_ps_print(mp, " [");
5110 mp_ps_pair_out(mp, txx,tyx);
5111 mp_ps_pair_out(mp, txy,tyy);
5112 mp_ps_print(mp, "0 0] t");
5113 } else if ((txx!=unity)||(tyy!=unity) ) {
5114 mp_ps_pair_out(mp,txx,tyy);
5115 mp_ps_print(mp, " s");
5117 mp_ps_print(mp, " S");
5118 if ( transformed ) mp_ps_print(mp, " Q");
5123 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5129 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5130 mp_ps_print_nl(mp, "");
5131 mp_ps_print_cmd(mp, "gsave ","q ");
5132 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5133 mp_ps_print(mp, "translate ");
5140 mp_ps_print_nl(mp, "");
5142 @<Adjust the transformation to account for |gs_width| and output the
5143 initial \&{gsave} if |transformed| should be |true|@>
5145 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5146 if ( gs_width!=unity ) {
5147 if ( gs_width==0 ) {
5148 txx=unity; tyy=unity;
5150 txx=mp_make_scaled(mp, txx,gs_width);
5151 txy=mp_make_scaled(mp, txy,gs_width);
5152 tyx=mp_make_scaled(mp, tyx,gs_width);
5153 tyy=mp_make_scaled(mp, tyy,gs_width);
5156 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5157 if ( (! transformed) ){
5158 mp_ps_print_cmd(mp, "gsave ","q ");
5163 @ @<Issue \ps\ commands to transform the coordinate system@>=
5164 if ( (txy!=0)||(tyx!=0) ){
5166 mp_ps_print_char(mp, '[');
5167 mp_ps_pair_out(mp, txx,tyx);
5168 mp_ps_pair_out(mp, txy,tyy);
5169 mp_ps_print(mp, "0 0] concat");
5170 } else if ( (txx!=unity)||(tyy!=unity) ){
5172 mp_ps_pair_out(mp, txx,tyy);
5173 mp_ps_print(mp, "scale");
5176 @ The \ps\ interpreter will probably abort if it encounters a singular
5177 transformation matrix. The determinant must be large enough to ensure that
5178 the printed representation will be nonsingular. Since the printed
5179 representation is always within $2^{-17}$ of the internal |scaled| value, the
5180 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5181 the magnitudes of |txx/65536|, |txy/65536|, etc.
5183 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5184 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5186 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5187 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5188 d1=4*aspect_bound+1;
5189 if ( abs(det)<d1 ) {
5190 if ( det>=0 ) { d1=d1-det; s=1; }
5191 else { d1=-d1-det; s=-1; };
5193 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5194 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5195 else txx=txx+(d1+s*abs(tyy)) / tyy;
5197 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5198 else txy=txy+(d1+s*abs(tyx)) / tyx;
5202 @ Here is a simple routine that just fills a cycle.
5205 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p);
5208 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p) { /* fill cyclic path~|p| */
5209 mp_gr_ps_path_out(mp, p);
5210 mp_ps_print_cmd(mp, " fill"," F");
5214 @ A text node may specify an arbitrary transformation but the usual case
5215 involves only shifting, scaling, and occasionally rotation. The purpose
5216 of |choose_scale| is to select a scale factor so that the remaining
5217 transformation is as ``nice'' as possible. The definition of ``nice''
5218 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5219 nice because they work out well for bitmap fonts. The code here selects
5220 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5221 non-shifting part of the transformation matrix. It is careful to avoid
5222 additions that might cause undetected overflow.
5225 scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) ;
5227 @ @c scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) {
5228 /* |p| should point to a text node */
5229 scaled a,b,c,d,ad,bc; /* temporary values */
5234 if ( (a<0) ) negate(a);
5235 if ( (b<0) ) negate(b);
5236 if ( (c<0) ) negate(c);
5237 if ( (d<0) ) negate(d);
5240 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5243 @ The potential overflow here is caused by the fact the returned value
5244 has to fit in a |name_type|, which is a quarterword.
5246 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
5249 quarterword mp_size_index (MP mp, font_number f, scaled s) ;
5252 quarterword mp_size_index (MP mp, font_number f, scaled s) {
5253 pointer p,q; /* the previous and current font size nodes */
5254 quarterword i; /* the size index for |q| */
5255 q=mp->font_sizes[f];
5258 if ( abs(s-sc_factor(q))<=fscale_tolerance )
5261 { p=q; q=link(q); incr(i); };
5262 if ( i==max_quarterword )
5263 mp_overflow(mp, "sizes per font",max_quarterword);
5264 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
5266 q=mp_get_node(mp, font_size_size);
5268 if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
5273 scaled mp_indexed_size (MP mp,font_number f, quarterword j);
5276 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
5277 pointer p; /* a font size node */
5278 quarterword i; /* the size index for |p| */
5279 p=mp->font_sizes[f];
5281 if ( p==null ) mp_confusion(mp, "size");
5284 if ( p==null ) mp_confusion(mp, "size");
5286 return sc_factor(p);
5290 void mp_clear_sizes (MP mp) ;
5292 @ @c void mp_clear_sizes (MP mp) {
5293 font_number f; /* the font whose size list is being cleared */
5294 pointer p; /* current font size nodes */
5295 for (f=null_font+1;f<=mp->last_fnum;f++) {
5296 while ( mp->font_sizes[f]!=null ) {
5297 p=mp->font_sizes[f];
5298 mp->font_sizes[f]=link(p);
5299 mp_free_node(mp, p,font_size_size);
5304 @ A text node may specify an arbitrary transformation but the usual case
5305 involves only shifting, scaling, and occasionally rotation. The purpose
5306 of |choose_scale| is to select a scale factor so that the remaining
5307 transformation is as ``nice'' as possible. The definition of ``nice''
5308 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5309 nice because they work out well for bitmap fonts. The code here selects
5310 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5311 non-shifting part of the transformation matrix. It is careful to avoid
5312 additions that might cause undetected overflow.
5314 @<Declare the \ps\ output procedures@>=
5315 scaled mp_choose_scale (MP mp, mp_graphic_object *p) ;
5317 @ @c scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
5318 /* |p| should point to a text node */
5319 scaled a,b,c,d,ad,bc; /* temporary values */
5324 if ( (a<0) ) negate(a);
5325 if ( (b<0) ) negate(b);
5326 if ( (c<0) ) negate(c);
5327 if ( (d<0) ) negate(d);
5330 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5333 @ There may be many sizes of one font and we need to keep track of the
5334 characters used for each size. This is done by keeping a linked list of
5335 sizes for each font with a counter in each text node giving the appropriate
5336 position in the size list for its font.
5338 @d font_size_size 2 /* size of a font size node */
5342 void mp_apply_mark_string_chars(MP mp, struct mp_edge_object *h, int next_size);
5345 void mp_apply_mark_string_chars(MP mp, struct mp_edge_object *h, int next_size) {
5346 struct mp_graphic_object * p;
5348 while ( p!= NULL ) {
5349 if ( gr_type(p)==mp_text_code ) {
5350 if ( gr_font_n(p)!=null_font ) {
5351 if ( gr_name_type(p)==next_size )
5352 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p));
5359 @ @<Unmark all marked characters@>=
5360 for (f=null_font+1;f<=mp->last_fnum;f++) {
5361 if ( mp->font_sizes[f]!=null ) {
5362 mp_unmark_font(mp, f);
5363 mp->font_sizes[f]=null;
5367 @ @<Scan all the text nodes and mark the used ...@>=
5370 if ( gr_type(p)==mp_text_code ) {
5372 if (f!=null_font ) {
5373 switch (prologues) {
5376 mp->font_sizes[f] = mp_void;
5377 mp_mark_string_chars(mp, f, gr_text_p(p));
5378 if (mp_has_fm_entry(mp,f,NULL) ) {
5379 if (mp->font_enc_name[f]==NULL )
5380 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
5381 mp->font_ps_name[f] = mp_fm_font_name(mp,f);
5385 mp->font_sizes[f]=mp_void;
5388 gr_name_type(p)=mp_size_index(mp, f,mp_choose_scale(mp, p));
5389 if ( gr_name_type(p)==0 )
5390 mp_mark_string_chars(mp, f, gr_text_p(p));
5399 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5401 @<Exported function headers@>=
5402 void mp_gr_ship_out (struct mp_edge_object *hh, int prologues, int procset) ;
5405 void mp_gr_ship_out (struct mp_edge_object *hh, int prologues, int procset) {
5406 struct mp_graphic_object *p;
5407 scaled ds,scf; /* design size and scale factor for a text node */
5408 font_number f; /* for loops over fonts while (un)marking characters */
5409 boolean transformed; /* is the coordinate system being transformed? */
5410 MP mp = hh->_parent;
5411 if (mp->history >= mp_fatal_error_stop ) return;
5413 prologues = (mp->internal[mp_prologues]>>16);
5415 procset = (mp->internal[mp_procset]>>16);
5416 mp_open_output_file(mp);
5417 mp_print_initial_comment(mp, hh, prologues);
5419 @<Unmark all marked characters@>;
5420 if ( prologues==2 || prologues==3 ) {
5421 mp_reload_encodings(mp);
5423 @<Scan all the text nodes and mark the used characters@>;
5424 if ( prologues==2 || prologues==3 ) {
5425 mp_print_improved_prologue(mp, hh, prologues, procset);
5427 mp_print_prologue(mp, hh, prologues, procset);
5429 mp_gs_unknown_graphics_state(mp, 0);
5432 if ( gr_has_color(p) ) {
5433 if ( (gr_pre_script(p))!=NULL ) {
5434 mp_ps_print_nl (mp, gr_pre_script(p));
5438 mp_gr_fix_graphics_state(mp, p);
5439 switch (gr_type(p)) {
5441 if ( gr_pen_p(p)==NULL ) {
5442 mp_gr_ps_fill_out(mp, gr_path_p(p));
5443 } else if ( pen_is_elliptical(gr_pen_p(p)) ) {
5444 mp_gr_stroke_ellipse(mp, p,true);
5446 mp_gr_ps_fill_out(mp, gr_path_p(p));
5447 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5449 if ( gr_post_script(p)!=NULL ) {
5450 mp_ps_print_nl (mp, gr_post_script(p));
5454 case mp_stroked_code:
5455 if ( pen_is_elliptical(gr_pen_p(p)) ) mp_gr_stroke_ellipse(mp, p,false);
5457 mp_gr_ps_fill_out(mp, gr_path_p(p));
5459 if ( gr_post_script(p)!=NULL ) {
5460 mp_ps_print_nl (mp, gr_post_script(p)); mp_ps_print_ln(mp);
5464 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5466 scf=mp_gr_choose_scale(mp, p);
5468 scf=mp_indexed_size(mp, gr_font_n(p), gr_name_type(p));
5469 @<Shift or transform as necessary before outputting text node~|p| at scale
5470 factor~|scf|; set |transformed:=true| if the original transformation must
5472 mp_ps_string_out(mp, gr_text_p(p));
5473 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5474 @<Print the size information and \ps\ commands for text node~|p|@>;
5477 if ( gr_post_script(p)!=NULL ) {
5478 mp_ps_print_nl (mp, gr_post_script(p)); mp_ps_print_ln(mp);
5481 case mp_start_clip_code:
5482 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5483 mp_gr_ps_path_out(mp, gr_path_p(p));
5484 mp_ps_print_cmd(mp, " clip"," W");
5486 if ( mp->internal[mp_restore_clip_color]>0 )
5487 mp_gs_unknown_graphics_state(mp, 1);
5489 case mp_stop_clip_code:
5490 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5492 if ( mp->internal[mp_restore_clip_color]>0 )
5493 mp_gs_unknown_graphics_state(mp, 2);
5495 mp_gs_unknown_graphics_state(mp, -1);
5497 case mp_start_bounds_code:
5498 case mp_stop_bounds_code:
5500 case mp_special_code:
5501 mp_ps_print_nl (mp, gr_pre_script(p));
5502 mp_ps_print_ln (mp);
5504 } /* all cases are enumerated */
5507 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
5508 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
5509 (mp->close_file)(mp,mp->ps_file);
5514 @ The envelope of a cyclic path~|q| could be computed by calling
5515 |make_envelope| once for |q| and once for its reversal. We don't do this
5516 because it would fail color regions that are covered by the pen regardless
5517 of where it is placed on~|q|.
5519 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5520 if ( gr_left_type(q)!=mp_endpoint ) {
5521 gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5522 gr_right_type(q)=mp_endpoint;
5527 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5529 mp_ps_print_char(mp, ' ');
5530 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5531 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5532 mp_ps_print(mp, " fshow");
5534 mp_ps_print_cmd(mp, " grestore"," Q")
5538 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5539 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5540 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5541 if ( transformed ) {
5542 mp_ps_print_cmd(mp, "gsave [", "q [");
5543 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5544 mp_make_scaled(mp, gr_tyx_val(p),scf));
5545 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5546 mp_make_scaled(mp, gr_tyy_val(p),scf));
5547 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5548 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5550 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5551 mp_ps_print_cmd(mp, "moveto","m");
5556 @ @<Exported function headers@>=
5557 void mp_gr_toss_objects ( struct mp_edge_object *hh) ;
5558 void mp_gr_toss_object (struct mp_graphic_object *p) ;
5561 void mp_gr_toss_object (struct mp_graphic_object *p) {
5562 switch (gr_type(p)) {
5564 mp_xfree(gr_pre_script(p));
5565 mp_xfree(gr_post_script(p));
5566 mp_gr_toss_knot_list(mp,gr_pen_p(p));
5567 mp_gr_toss_knot_list(mp,gr_path_p(p));
5568 mp_gr_toss_knot_list(mp,gr_htap_p(p));
5570 case mp_stroked_code:
5571 mp_xfree(gr_pre_script(p));
5572 mp_xfree(gr_post_script(p));
5573 mp_gr_toss_knot_list(mp,gr_pen_p(p));
5574 mp_gr_toss_knot_list(mp,gr_path_p(p));
5575 if (gr_dash_p(p)!=NULL)
5576 mp_gr_toss_dashes (mp,gr_dash_p(p));
5579 mp_xfree(gr_pre_script(p));
5580 mp_xfree(gr_post_script(p));
5581 mp_xfree(gr_text_p(p));
5583 case mp_start_clip_code:
5584 case mp_start_bounds_code:
5585 mp_gr_toss_knot_list(mp,gr_path_p(p));
5587 case mp_stop_clip_code:
5588 case mp_stop_bounds_code:
5590 case mp_special_code:
5591 mp_xfree(gr_pre_script(p));
5593 } /* all cases are enumerated */
5599 void mp_gr_toss_objects (struct mp_edge_object *hh) {
5600 struct mp_graphic_object *p, *q;
5604 mp_gr_toss_object(p);