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) {
548 p = (enc_entry *) pa;
550 mp_xfree (p->file_name);
551 if (p->glyph_names != NULL)
552 for (i = 0; i < 256; i++)
553 if (p->glyph_names[i] != notdef)
554 mp_xfree (p->glyph_names[i]);
555 mp_xfree (p->glyph_names);
560 static void enc_free (MP mp);
562 @ @c static void enc_free (MP mp) {
563 if (mp->ps->enc_tree != NULL)
564 avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
567 @ @<Exported function headers@>=
568 void mp_reload_encodings (MP mp) ;
571 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) ;
573 @ @c void mp_reload_encodings (MP mp) {
577 int lastfnum = mp->last_fnum;
578 for (f=null_font+1;f<=lastfnum;f++) {
579 if (mp->font_enc_name[f]!=NULL ) {
580 mp_xfree(mp->font_enc_name[f]);
581 mp->font_enc_name[f]=NULL;
583 if (mp_has_fm_entry (mp,f,&fm_cur)) {
584 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
585 e = fm_cur->encoding;
591 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) {
595 for (f=null_font+1;f<=lastfnum;f++) {
596 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
597 if (fm != NULL && (fm->ps_name != NULL)) {
598 if (is_reencoded (fm)) {
599 if (encodings_only || (!is_subsetted (fm))) {
601 mp_write_enc (mp,NULL, e);
602 /* clear for next run */
611 @* \[44b] Parsing font map files.
619 @d fm_close() (mp->close_file)(mp,mp->ps->fm_file)
620 @d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file)
623 int fm_getchar(MP mp) {
625 unsigned char byte=0;
626 void *byte_ptr = &byte;
627 (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
635 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
636 enum _ltype { MAPFILE, MAPLINE };
637 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
638 typedef struct mitem {
639 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
640 int type; /* map file or map line */
641 char *map_line; /* pointer to map file name or map line */
642 int lineno; /* line number in map file */
648 fm_entry *loaded_tfm_found;
649 fm_entry *avail_tfm_found;
650 fm_entry *non_tfm_found;
651 fm_entry *not_avail_tfm_found;
653 @ @<Set initial...@>=
654 mp->ps->mitem = NULL;
657 static const char nontfm[] = "<nontfm>";
660 @d read_field(r, q, buf) do {
662 while (*r != ' ' && *r != '\0')
670 fm->F = mp_xstrdup(mp,buf);
682 static fm_entry *new_fm_entry (MP mp) {
684 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
689 fm->subset_tag = NULL;
691 fm->tfm_num = null_font;
692 fm->tfm_avail = TFM_UNCHECKED;
700 fm->all_glyphs = false;
707 static void delete_fm_entry (fm_entry * fm) {
708 mp_xfree (fm->tfm_name);
709 mp_xfree (fm->ps_name);
710 mp_xfree (fm->ff_name);
711 mp_xfree (fm->subset_tag);
712 mp_xfree (fm->charset);
716 static ff_entry *new_ff_entry (MP mp) {
718 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
724 static void delete_ff_entry (ff_entry * ff) {
725 mp_xfree (ff->ff_name);
726 mp_xfree (ff->ff_path);
730 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
731 static char buf[SMALL_BUF_SIZE];
732 char *p = tfmname, *r = strend (p) - 1, *q = r;
733 while (q > p && isdigit (*q))
735 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
737 check_buf (q - p + 1, SMALL_BUF_SIZE);
738 strncpy (buf, p, (size_t) (q - p));
744 @ @<Exported function headers@>=
745 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
748 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
749 fm_entry *res = NULL;
750 res = mp_fm_lookup (mp, f);
754 return (res != NULL);
758 struct avl_table *tfm_tree;
759 struct avl_table *ps_tree;
760 struct avl_table *ff_tree;
762 @ @<Set initial...@>=
763 mp->ps->tfm_tree = NULL;
764 mp->ps->ps_tree = NULL;
765 mp->ps->ff_tree = NULL;
767 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
770 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
772 return strcmp (((const fm_entry *) pa)->tfm_name,
773 ((const fm_entry *) pb)->tfm_name);
776 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
778 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
780 const fm_entry *p1 = (const fm_entry *) pa, *p2 = (const fm_entry *) pb;
782 assert (p1->ps_name != NULL && p2->ps_name != NULL);
783 if ((i = strcmp (p1->ps_name, p2->ps_name)))
785 cmp_return (p1->slant, p2->slant);
786 cmp_return (p1->extend, p2->extend);
787 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
788 (i = strcmp (p1->tfm_name, p2->tfm_name)))
793 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
795 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
797 return strcmp (((const ff_entry *) pa)->ff_name,
798 ((const ff_entry *) pb)->ff_name);
801 @ @c static void create_avl_trees (MP mp) {
802 if (mp->ps->tfm_tree == NULL) {
803 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
804 assert (mp->ps->tfm_tree != NULL);
806 if (mp->ps->ps_tree == NULL) {
807 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
808 assert (mp->ps->ps_tree != NULL);
810 if (mp->ps->ff_tree == NULL) {
811 mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
812 assert (mp->ps->ff_tree != NULL);
816 @ The function |avl_do_entry| is not completely symmetrical with regards
817 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
818 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
819 with the original version.
823 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
824 @d set_pslink(fm) ((fm)->links |= LINK_PS)
825 @d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
826 @d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
827 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
828 @d has_pslink(fm) ((fm)->links & LINK_PS)
831 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
837 /* handle |tfm_name| link */
839 if (strcmp (fp->tfm_name, nontfm)) {
840 p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
842 if (mode == FM_DUPIGNORE) {
843 snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
847 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
848 if (mp_has_font_size(mp,p->tfm_num)) {
850 "fontmap entry for `%s' has been used, replace/delete not allowed",
855 a = avl_delete (mp->ps->tfm_tree, p);
862 if (mode != FM_DELETE) {
863 aa = avl_probe (mp->ps->tfm_tree, fp);
869 /* handle |ps_name| link */
871 if (fp->ps_name != NULL) {
872 assert (fp->tfm_name != NULL);
873 p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
875 if (mode == FM_DUPIGNORE) {
877 "ps_name entry for `%s' already exists, duplicates ignored",
881 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
882 if (mp_has_font_size(mp,p->tfm_num)) {
883 /* REPLACE/DELETE not allowed */
885 "fontmap entry for `%s' has been used, replace/delete not allowed",
890 a = avl_delete (mp->ps->ps_tree, p);
893 if (!has_tfmlink (p))
897 if (mode != FM_DELETE) {
898 aa = avl_probe (mp->ps->ps_tree, fp);
904 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
905 return 1; /* deallocation of |fm_entry| structure required */
910 @ consistency check for map entry, with warn flag
913 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
917 if (fm->ps_name != NULL) {
918 if (is_basefont (fm)) {
919 if (is_fontfile (fm) && !is_included (fm)) {
921 snprintf(s,128, "invalid entry for `%s': "
922 "font file must be included or omitted for base fonts",
928 } else { /* not a base font */
929 /* if no font file given, drop this entry */
930 /* |if (!is_fontfile (fm)) {
933 "invalid entry for `%s': font file missing",
942 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
945 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
951 if ((fm->slant != 0 || fm->extend != 0) &&
952 (is_truetype (fm))) {
955 "invalid entry for `%s': "
956 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
962 if (abs (fm->slant) > 1000) {
965 "invalid entry for `%s': too big value of SlantFont (%g)",
966 fm->tfm_name, fm->slant / 1000.0);
971 if (abs (fm->extend) > 2000) {
974 "invalid entry for `%s': too big value of ExtendFont (%g)",
975 fm->tfm_name, fm->extend / 1000.0);
981 !(is_truetype (fm) && is_included (fm) &&
982 is_subsetted (fm) && !is_reencoded (fm))) {
985 "invalid entry for `%s': "
986 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
995 @ returns true if s is one of the 14 std. font names; speed-trimmed.
997 @c static boolean check_basefont (char *s) {
998 static const char *basefont_names[] = {
1000 "Courier-Bold", /* 1:12 */
1001 "Courier-Oblique", /* 2:15 */
1002 "Courier-BoldOblique", /* 3:19 */
1003 "Helvetica", /* 4:9 */
1004 "Helvetica-Bold", /* 5:14 */
1005 "Helvetica-Oblique", /* 6:17 */
1006 "Helvetica-BoldOblique", /* 7:21 */
1008 "Times-Roman", /* 9:11 */
1009 "Times-Bold", /* 10:10 */
1010 "Times-Italic", /* 11:12 */
1011 "Times-BoldItalic", /* 12:16 */
1012 "ZapfDingbats" /* 13:12 */
1014 static const int Index[] =
1015 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1018 const size_t n = strlen (s);
1022 if (n == 12) { /* three names have length 12 */
1025 k = 1; /* Courier-Bold */
1028 k = 11; /* Times-Italic */
1031 k = 13; /* ZapfDingbats */
1038 if (k > -1 && !strcmp (basefont_names[k], s))
1044 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1046 @c static void fm_scan_line (MP mp) {
1047 int a, b, c, j, u = 0, v = 0;
1050 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1051 char *p, *q, *r, *s;
1053 switch (mp->ps->mitem->type) {
1057 c = fm_getchar (mp);
1058 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1064 r = mp->ps->mitem->map_line;
1069 if (*r == '\0' || is_cfg_comment (*r))
1071 fm = new_fm_entry (mp);
1072 read_field (r, q, buf);
1073 set_field (tfm_name);
1075 read_field (r, q, buf);
1076 if (*buf != '<' && *buf != '"')
1077 set_field (ps_name);
1079 r = p; /* unget the field */
1080 if (isdigit (*r)) { /* font flags given */
1081 fm->flags = atoi (r);
1082 while (isdigit (*r))
1085 while (1) { /* loop through "specials", encoding, font file */
1090 case '"': /* opening quote */
1095 if (sscanf (r, "%f %n", &d, &j) > 0) {
1096 s = r + j; /* jump behind number, eat also blanks, if any */
1097 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1098 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1099 if (str_prefix (s, "SlantFont")) {
1100 d *= 1000.0; /* correct rounding also for neg. numbers */
1101 fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1102 r = s + strlen ("SlantFont");
1103 } else if (str_prefix (s, "ExtendFont")) {
1105 fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1106 if (fm->extend == 1000)
1108 r = s + strlen ("ExtendFont");
1109 } else { /* unknown name */
1111 *r != ' ' && *r != '"' && *r != '\0';
1112 r++); /* jump over name */
1113 c = *r; /* remember char for temporary end of string */
1115 snprintf(warn_s,128,
1116 "invalid entry for `%s': unknown name `%s' ignored",
1122 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1125 if (*r == '"') /* closing quote */
1128 snprintf(warn_s,128,
1129 "invalid entry for `%s': closing quote missing",
1135 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1136 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1142 default: /* encoding or font file specification */
1146 if (*r == '<' || *r == '[')
1149 read_field (r, q, buf);
1150 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1151 if (strlen (buf) > 4 && strcasecmp (strend (buf) - 4, ".enc") == 0) {
1152 fm->encoding = mp_add_enc (mp, buf);
1153 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1154 } else if (strlen (buf) > 0) { /* file name given */
1155 /* font file, formats:
1156 * subsetting: '<cmr10.pfa'
1157 * no subsetting: '<<cmr10.pfa'
1158 * no embedding: 'cmr10.pfa'
1160 if (a == '<' || u == '<') {
1162 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1164 /* otherwise b == '<' (or '[') => no subsetting */
1166 set_field (ff_name);
1175 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1177 if (is_fontfile (fm)
1178 && strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1180 if (check_fm_entry (mp,fm, true) != 0)
1183 Until here the map line has been completely scanned without errors;
1184 fm points to a valid, freshly filled-out |fm_entry| structure.
1185 Now follows the actual work of registering/deleting.
1187 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
1190 delete_fm_entry (fm);
1194 @c static void fm_read_info (MP mp) {
1197 if (mp->ps->tfm_tree == NULL)
1198 create_avl_trees (mp);
1199 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1201 mp->ps->mitem->lineno = 1;
1202 switch (mp->ps->mitem->type) {
1204 n = mp->ps->mitem->map_line;
1205 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1206 if (!mp->ps->fm_file) {
1207 snprintf(s,256,"cannot open font map file %s",n);
1210 int save_selector = mp->selector;
1211 mp_normalize_selector(mp);
1214 while (!fm_eof ()) {
1216 mp->ps->mitem->lineno++;
1220 mp->selector = save_selector;
1221 mp->ps->fm_file = NULL;
1231 mp->ps->mitem->map_line = NULL; /* done with this line */
1236 static scaled mp_round_xn_over_d (MP mp, scaled x, integer n, integer d) {
1237 boolean positive; /* was |x>=0|? */
1238 unsigned int t,u; /* intermediate quantities */
1239 integer v; /* intermediate quantities */
1243 negate(x); positive=false;
1246 u=(x / 0100000)*n+(t / 0100000);
1247 v=(u % d)*0100000 + (t % 0100000);
1248 if ( u / d>=0100000 ) mp->arith_error=true;
1249 else u=0100000*(u / d) + (v / d);
1253 return ( positive ? u : -u );
1255 static fm_entry *mk_ex_fm (MP mp, font_number f, fm_entry * basefm, int ex) {
1257 integer e = basefm->extend;
1260 fm = new_fm_entry (mp);
1261 fm->flags = basefm->flags;
1262 fm->encoding = basefm->encoding;
1263 fm->type = basefm->type;
1264 fm->slant = basefm->slant;
1265 fm->extend = mp_round_xn_over_d (mp, e, 1000 + ex, 1000);
1266 /* modify ExtentFont to simulate expansion */
1267 if (fm->extend == 1000)
1269 fm->tfm_name = mp_xstrdup (mp,mp->font_name[f]);
1270 if (basefm->ps_name != NULL)
1271 fm->ps_name = mp_xstrdup (mp,basefm->ps_name);
1272 fm->ff_name = mp_xstrdup (mp,basefm->ff_name);
1275 fm->tfm_avail = TFM_FOUND;
1276 assert (strcmp (fm->tfm_name, nontfm));
1280 @ @c static void init_fm (fm_entry * fm, font_number f) {
1281 if (fm->tfm_num == null_font ) {
1283 fm->tfm_avail = TFM_FOUND;
1288 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1291 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1293 fm_entry *fm, *exfm;
1296 if (mp->ps->tfm_tree == NULL)
1297 fm_read_info (mp); /* only to read default map file */
1298 tfm = mp->font_name[f];
1299 assert (strcmp (tfm, nontfm));
1300 /* Look up for full <tfmname>[+-]<expand> */
1302 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1305 return (fm_entry *) fm;
1307 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1308 if (tfm == NULL) /* not an expanded font, nothing to do */
1312 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1313 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1314 return (fm_entry *) fm; /* font expansion uses the base font */
1315 /* the following code would be obsolete, as would be |mk_ex_fm| */
1316 if (!is_t1fontfile (fm) || !is_included (fm)) {
1319 "font %s cannot be expanded (not an included Type1 font)", tfm);
1323 exfm = mk_ex_fm (mp, f, fm, e); /* copies all fields from fm except tfm name */
1325 ai = avl_do_entry (mp, exfm, FM_DUPIGNORE);
1327 return (fm_entry *) exfm;
1332 @ Early check whether a font file exists. Used e. g. for replacing fonts
1333 of embedded PDF files: Without font file, the font within the embedded
1334 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1335 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1336 only once per font file name + expansion parameter. This might help
1337 keeping speed, if many PDF pages with same fonts are to be embedded.
1339 The |ff_tree| contains only font files, which are actually needed,
1340 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1343 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1348 assert (fm->ff_name != NULL);
1349 tmp.ff_name = fm->ff_name;
1350 ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1351 if (ff == NULL) { /* not yet in database */
1352 ff = new_ff_entry (mp);
1353 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1354 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1355 aa = avl_probe (mp->ps->ff_tree, ff);
1356 assert (aa != NULL);
1361 @ Process map file given by its name or map line contents. Items not
1362 beginning with [+-=] flush default map file, if it has not yet been
1363 read. Leading blanks and blanks immediately following [+-=] are ignored.
1366 @c static void mp_process_map_item (MP mp, char *s, int type) {
1370 s++; /* ignore leading blank */
1372 case '+': /* +mapfile.map, +mapline */
1373 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1376 case '=': /* =mapfile.map, =mapline */
1377 mode = FM_REPLACE; /* try to replace earlier entry */
1380 case '-': /* -mapfile.map, -mapline */
1381 mode = FM_DELETE; /* try to delete entry */
1385 mode = FM_DUPIGNORE; /* like +, but also: */
1386 mp_xfree(mp->ps->mitem->map_line);
1387 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1390 s++; /* ignore blank after [+-=] */
1391 p = s; /* map item starts here */
1393 case MAPFILE: /* remove blank at end */
1394 while (*p != '\0' && *p != ' ')
1398 case MAPLINE: /* blank at end allowed */
1403 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1405 if (*s != '\0') { /* only if real item to process */
1406 mp->ps->mitem->mode = mode;
1407 mp->ps->mitem->type = type;
1408 mp->ps->mitem->map_line = s;
1413 @ @<Exported function headers@>=
1414 void mp_map_file (MP mp, str_number t);
1415 void mp_map_line (MP mp, str_number t);
1416 void mp_init_map_file (MP mp, int is_troff);
1419 void mp_map_file (MP mp, str_number t) {
1420 char *s = mp_xstrdup(mp,mp_str (mp,t));
1421 mp_process_map_item (mp, s, MAPFILE);
1424 void mp_map_line (MP mp, str_number t) {
1425 char *s = mp_xstrdup(mp,mp_str (mp,t));
1426 mp_process_map_item (mp, s, MAPLINE);
1431 @c void mp_init_map_file (MP mp, int is_troff) {
1433 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1434 mp->ps->mitem->mode = FM_DUPIGNORE;
1435 mp->ps->mitem->type = MAPFILE;
1436 mp->ps->mitem->map_line = NULL;
1437 r = (mp->find_file)(mp,"mpost.map", "rb", mp_filetype_fontmap);
1440 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1443 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1445 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1450 @ @<Dealloc variables@>=
1451 if (mp->ps->mitem!=NULL) {
1452 mp_xfree(mp->ps->mitem->map_line);
1453 mp_xfree(mp->ps->mitem);
1459 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1462 fm = (fm_entry *) pa;
1463 if (!has_pslink (fm))
1464 delete_fm_entry (fm);
1468 static void destroy_fm_entry_ps (void *pa, void *pb) {
1471 fm = (fm_entry *) pa;
1472 if (!has_tfmlink (fm))
1473 delete_fm_entry (fm);
1477 static void destroy_ff_entry (void *pa, void *pb) {
1480 ff = (ff_entry *) pa;
1481 delete_ff_entry (ff);
1485 static void fm_free (MP mp);
1488 static void fm_free (MP mp) {
1489 if (mp->ps->tfm_tree != NULL)
1490 avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1491 if (mp->ps->ps_tree != NULL)
1492 avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1493 if (mp->ps->ff_tree != NULL)
1494 avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1497 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1498 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1499 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
1500 one or more spaces, a PostScript name, and possibly a space and some other junk.
1501 This routine reads the table, updates |font_ps_name| entries starting after
1502 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
1504 @d ps_tab_name "psfonts.map" /* locates font name translation table */
1507 static void mp_read_psname_table (MP mp) ;
1510 static void mp_read_psname_table (MP mp) {
1512 if (mp->ps->mitem == NULL) {
1513 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1514 mp->ps->mitem->mode = FM_DUPIGNORE;
1515 mp->ps->mitem->type = MAPFILE;
1516 mp->ps->mitem->map_line = NULL;
1518 mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name);
1520 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1521 if (mp_has_fm_entry(mp, k, NULL)) {
1522 mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1525 mp->last_ps_fnum=mp->last_fnum;
1529 @ The traditional function is a lot shorter now.
1533 @* \[44c] Helper functions for Type1 fonts.
1536 typedef char char_entry;
1537 typedef unsigned char Byte;
1541 char_entry *char_ptr, *char_array;
1543 char *job_id_string;
1545 @ @<Set initial...@>=
1546 mp->ps->char_array = NULL;
1547 mp->ps->job_id_string = NULL;
1550 @d SMALL_ARRAY_SIZE 256
1554 void mp_set_job_id (MP mp) {
1555 char *name_string, *format_string, *s;
1558 if (mp->ps->job_id_string != NULL)
1560 if ( mp->job_name==NULL )
1561 mp->job_name = mp_xstrdup(mp,"mpout");
1562 name_string = mp_xstrdup (mp,mp->job_name);
1563 format_string = mp_xstrdup (mp,mp->mem_ident);
1564 slen = SMALL_BUF_SIZE +
1565 strlen (name_string) +
1566 strlen (format_string);
1567 s = mp_xmalloc (mp,slen, sizeof (char));
1568 i = snprintf (s, slen,
1569 "%.4d/%.2d/%.2d %.2d:%.2d %s %s",
1570 (mp->internal[mp_year]>>16),
1571 (mp->internal[mp_month]>>16),
1572 (mp->internal[mp_day]>>16),
1573 (mp->internal[mp_time]>>16) / 60,
1574 (mp->internal[mp_time]>>16) % 60,
1575 name_string, format_string);
1576 mp->ps->job_id_string = mp_xstrdup (mp,s);
1578 mp_xfree (name_string);
1579 mp_xfree (format_string);
1581 static void fnstr_append (MP mp, const char *s) {
1582 size_t l = strlen (s) + 1;
1583 alloc_array (char, l, SMALL_ARRAY_SIZE);
1584 strcat (mp->ps->char_ptr, s);
1585 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1588 @ @<Exported function headers@>=
1589 void mp_set_job_id (MP mp) ;
1591 @ @<Dealloc variables@>=
1592 mp_xfree(mp->ps->job_id_string);
1594 @ this is not really a true crc32, but it should be just enough to keep
1595 subsets prefixes somewhat disjunct
1598 static unsigned long crc32 (int oldcrc, const Byte *buf, int len) {
1599 unsigned long ret = 0;
1602 ret = (23<<24)+(45<<16)+(67<<8)+89;
1605 ret = (ret<<2)+buf[i];
1608 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1609 integer b; /* |char_base[f]| */
1611 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1617 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, int tex_font)
1623 if (mp->ps->job_id_string ==NULL)
1624 mp_fatal_error(mp, "no job id!");
1625 l = strlen (mp->ps->job_id_string) + 1;
1627 alloc_array (char, l, SMALL_ARRAY_SIZE);
1628 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1629 mp->ps->char_ptr = strend (mp->ps->char_array);
1630 if (fm_cur->tfm_name != NULL) {
1631 fnstr_append (mp," TFM name: ");
1632 fnstr_append (mp,fm_cur->tfm_name);
1634 fnstr_append (mp," PS name: ");
1635 if (fm_cur->ps_name != NULL)
1636 fnstr_append (mp,fm_cur->ps_name);
1637 fnstr_append (mp," Encoding: ");
1638 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1639 fnstr_append (mp,(fm_cur->encoding)->file_name);
1641 fnstr_append (mp,"built-in");
1642 fnstr_append (mp," CharSet: ");
1643 for (i = 0; i < 256; i++)
1644 if (mp_char_marked (mp,tex_font, i) && glyph_names[i] != notdef) {
1645 if (glyph_names[i]!=NULL) {
1646 fnstr_append (mp,"/");
1647 fnstr_append (mp,glyph_names[i]);
1650 if (fm_cur->charset != NULL) {
1651 fnstr_append (mp," Extra CharSet: ");
1652 fnstr_append (mp, fm_cur->charset);
1654 crc = crc32 (0L, Z_NULL, 0);
1655 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1656 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1657 * there are 26 uppercase chars ==> each char represents a number in range
1658 * |0..25|. The maximal number that can be represented by the tag is
1659 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1660 * of the CRC must be dropped out.
1662 for (i = 0; i < 6; i++) {
1663 tag[i] = 'A' + crc % 26;
1667 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1673 @d external_enc() (fm_cur->encoding)->glyph_names
1674 @d is_used_char(c) mp_char_marked (mp, tex_font, c)
1675 @d end_last_eexec_line()
1676 mp->ps->hexline_length = HEXLINE_WIDTH;
1678 mp->ps->t1_eexec_encrypt = false
1679 @d t1_log(s) mp_print(mp,(char *)s)
1680 @d t1_putchar(c) wps_chr(c)
1681 @d embed_all_glyphs(tex_font) false
1683 @d extra_charset() mp->ps->dvips_extra_charset
1684 @d update_subset_tag()
1685 @d fixedcontent true
1688 #define PRINTF_BUF_SIZE 1024
1689 char *dvips_extra_charset;
1691 unsigned char *grid;
1692 char *ext_glyph_names[256];
1693 char print_buf[PRINTF_BUF_SIZE];
1694 int t1_byte_waiting;
1696 @ @<Set initial ...@>=
1697 mp->ps->dvips_extra_charset=NULL;
1698 mp->ps->t1_byte_waiting=0;
1701 @d t1_ungetchar(A) mp->ps->t1_byte_waiting=A
1702 @d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file)
1703 @d t1_close() (mp->close_file)(mp,mp->ps->t1_file)
1704 @d valid_code(c) (c >= 0 && c < 256)
1707 int t1_getchar (MP mp) {
1709 unsigned char byte=0;
1710 void *byte_ptr = &byte;
1711 if (mp->ps->t1_byte_waiting) {
1712 byte = mp->ps->t1_byte_waiting;
1713 mp->ps->t1_byte_waiting = 0;
1715 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
1720 @ @<Static variables in the outer block@>=
1721 static const char *standard_glyph_names[256] =
1722 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1723 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1724 notdef, notdef, notdef, notdef, notdef, notdef,
1725 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1726 "space", "exclam", "quotedbl", "numbersign",
1727 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1728 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1729 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1730 "eight", "nine", "colon", "semicolon", "less",
1731 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1732 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1733 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1734 "backslash", "bracketright", "asciicircum", "underscore",
1735 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1736 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1737 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1738 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1739 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1740 notdef, notdef, notdef, notdef, notdef, notdef,
1741 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1742 notdef, notdef, notdef, "exclamdown", "cent",
1743 "sterling", "fraction", "yen", "florin", "section", "currency",
1744 "quotesingle", "quotedblleft", "guillemotleft",
1745 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1746 "dagger", "daggerdbl", "periodcentered", notdef,
1747 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1748 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1749 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1750 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1751 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1752 notdef, notdef, notdef, notdef, notdef, notdef,
1753 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1754 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1755 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1756 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1757 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1758 "germandbls", notdef, notdef, notdef, notdef };
1759 static const char charstringname[] = "/CharStrings";
1762 char **t1_glyph_names;
1763 char *t1_builtin_glyph_names[256];
1764 char charsetstr[0x4000];
1765 boolean read_encoding_only;
1769 #define T1_BUF_SIZE 0x10
1773 #define CS_VMOVETO 4
1774 #define CS_RLINETO 5
1775 #define CS_HLINETO 6
1776 #define CS_VLINETO 7
1777 #define CS_RRCURVETO 8
1778 #define CS_CLOSEPATH 9
1779 #define CS_CALLSUBR 10
1780 #define CS_RETURN 11
1781 #define CS_ESCAPE 12
1783 #define CS_ENDCHAR 14
1784 #define CS_RMOVETO 21
1785 #define CS_HMOVETO 22
1786 #define CS_VHCURVETO 30
1787 #define CS_HVCURVETO 31
1788 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1790 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1791 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1792 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1793 #define CS_SEAC CS_1BYTE_MAX + 6
1794 #define CS_SBW CS_1BYTE_MAX + 7
1795 #define CS_DIV CS_1BYTE_MAX + 12
1796 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1797 #define CS_POP CS_1BYTE_MAX + 17
1798 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1799 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1800 #define CS_MAX CS_2BYTE_MAX
1803 typedef unsigned char byte;
1805 byte nargs; /* number of arguments */
1806 boolean bottom; /* take arguments from bottom of stack? */
1807 boolean clear; /* clear stack? */
1809 } cc_entry; /* CharString Command */
1811 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1813 unsigned short len; /* length of the whole string */
1814 unsigned short cslen; /* length of the encoded part of the string */
1820 unsigned short t1_dr, t1_er;
1821 unsigned short t1_c1, t1_c2;
1822 unsigned short t1_cslen;
1825 @ @<Set initial...@>=
1826 mp->ps->t1_c1 = 52845;
1827 mp->ps->t1_c2 = 22719;
1830 typedef char t1_line_entry;
1831 typedef char t1_buf_entry;
1834 t1_line_entry *t1_line_ptr, *t1_line_array;
1835 size_t t1_line_limit;
1836 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1837 size_t t1_buf_limit;
1839 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1840 char *cs_dict_start, *cs_dict_end;
1841 int cs_count, cs_size, cs_size_pos;
1843 char *subr_array_start, *subr_array_end;
1844 int subr_max, subr_size, subr_size_pos;
1846 @ @<Set initial...@>=
1847 mp->ps->t1_line_array = NULL;
1848 mp->ps->t1_buf_array = NULL;
1851 This list contains the begin/end tokens commonly used in the
1852 /Subrs array of a Type 1 font.
1854 @<Static variables in the outer block@>=
1855 static const char *cs_token_pairs_list[][2] = {
1858 {" RD", "noaccess put"},
1859 {" -|", "noaccess put"},
1864 const char **cs_token_pair;
1865 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1866 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1867 long t1_block_length;
1875 @<Set initial ...@>=
1876 mp->ps->hexline_length = HEXLINE_WIDTH;
1879 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1880 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1881 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1882 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1883 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1884 @d t1_subrs() t1_prefix("/Subrs")
1885 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1886 @d t1_cleartomark() t1_prefix("cleartomark")
1888 @d isdigit(A) ((A)>='0'&&(A)<='9')
1891 static void end_hexline (MP mp) {
1892 if (mp->ps->hexline_length == HEXLINE_WIDTH) {
1894 mp->ps->hexline_length = 0;
1897 static void t1_check_pfa (MP mp) {
1898 const int c = t1_getchar (mp);
1899 mp->ps->t1_pfa = (c != 128) ? true : false;
1902 static int t1_getbyte (MP mp)
1904 int c = t1_getchar (mp);
1907 if (mp->ps->t1_block_length == 0) {
1909 mp_fatal_error (mp, "invalid marker");
1910 c = t1_getchar (mp);
1916 mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
1917 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 8;
1918 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 16;
1919 mp->ps->t1_block_length |= (t1_getchar (mp) & 0xff) << 24;
1920 c = t1_getchar (mp);
1922 mp->ps->t1_block_length--;
1925 static int hexval (int c) {
1926 if (c >= 'A' && c <= 'F')
1927 return c - 'A' + 10;
1928 else if (c >= 'a' && c <= 'f')
1929 return c - 'a' + 10;
1930 else if (c >= '0' && c <= '9')
1935 static byte edecrypt (MP mp, byte cipher) {
1937 if (mp->ps->t1_pfa) {
1938 while (cipher == 10 || cipher == 13)
1939 cipher = t1_getbyte (mp);
1940 mp->ps->last_hexbyte = cipher = (hexval (cipher) << 4) + hexval (t1_getbyte (mp));
1942 plain = (cipher ^ (mp->ps->t1_dr >> 8));
1943 mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1946 static byte cdecrypt (MP mp, byte cipher, unsigned short *cr)
1948 const byte plain = (cipher ^ (*cr >> 8));
1949 *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1952 static byte eencrypt (MP mp, byte plain)
1954 const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1955 mp->ps->t1_er = (cipher + mp->ps->t1_er) * mp->ps->t1_c1 + mp->ps->t1_c2;
1959 static byte cencrypt (MP mp, byte plain, unsigned short *cr)
1961 const byte cipher = (plain ^ (*cr >> 8));
1962 *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1966 static char *eol (char *s) {
1967 char *p = strend (s);
1968 if (p - s > 1 && p[-1] != 10) {
1974 static float t1_scan_num (MP mp, char *p, char **r)
1979 if (sscanf (p, "%g", &f) != 1) {
1980 remove_eol (p, mp->ps->t1_line_array);
1981 snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1982 mp_fatal_error(mp,s);
1985 for (; isdigit (*p) || *p == '.' ||
1986 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1992 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1995 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1998 while (s1 >= begin_buf && s2 >= s) {
2007 @d alloc_array(T, n, s) do {
2008 if (mp->ps->T##_array == NULL) {
2009 mp->ps->T##_limit = (s);
2010 if ((unsigned)(n) > mp->ps->T##_limit)
2011 mp->ps->T##_limit = (n);
2012 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
2013 mp->ps->T##_ptr = mp->ps->T##_array;
2015 else if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
2016 size_t last_ptr_index;
2017 last_ptr_index = mp->ps->T##_ptr - mp->ps->T##_array;
2018 mp->ps->T##_limit *= 2;
2019 if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
2020 mp->ps->T##_limit = mp->ps->T##_ptr - mp->ps->T##_array + (n);
2021 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
2022 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
2026 @d out_eexec_char(A) t1_outhex(mp,(A))
2029 static void t1_outhex (MP mp, byte b)
2031 static char *hexdigits = "0123456789ABCDEF";
2032 t1_putchar (hexdigits[b / 16]);
2033 t1_putchar (hexdigits[b % 16]);
2034 mp->ps->hexline_length += 2;
2037 static void t1_getline (MP mp) {
2038 int c, l, eexec_scan;
2040 static const char eexec_str[] = "currentfile eexec";
2041 static int eexec_len = 17; /* |strlen(eexec_str)| */
2044 mp_fatal_error (mp,"unexpected end of file");
2045 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2046 alloc_array (t1_line, 1, T1_BUF_SIZE);
2047 mp->ps->t1_cslen = 0;
2049 c = t1_getbyte (mp);
2052 while (!t1_eof ()) {
2053 if (mp->ps->t1_in_eexec == 1)
2054 c = edecrypt (mp,c);
2055 alloc_array (t1_line, 1, T1_BUF_SIZE);
2056 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2057 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
2058 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
2063 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2065 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
2066 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2067 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2068 p = mp->ps->t1_line_ptr - 5;
2071 mp->ps->t1_cslen = l = t1_scan_num (mp, p + 1, 0);
2072 mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2073 /* |mp->ps->cs_start| is an index now */
2074 alloc_array (t1_line, l, T1_BUF_SIZE);
2076 *mp->ps->t1_line_ptr++ = edecrypt (mp,t1_getbyte (mp));
2078 c = t1_getbyte (mp);
2080 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
2081 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2082 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2084 if (eexec_scan == eexec_len)
2085 mp->ps->t1_in_eexec = 1;
2087 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2088 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2089 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2092 static void t1_putline (MP mp)
2094 char *p = mp->ps->t1_line_array;
2095 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2097 if (mp->ps->t1_eexec_encrypt) {
2098 while (p < mp->ps->t1_line_ptr)
2099 out_eexec_char (eencrypt (mp,*p++));
2101 while (p < mp->ps->t1_line_ptr)
2106 static void t1_puts (MP mp, const char *s)
2108 if (s != mp->ps->t1_line_array)
2109 strcpy (mp->ps->t1_line_array, s);
2110 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2114 static void t1_printf (MP mp, const char *fmt, ...)
2117 va_start (args, fmt);
2118 vsprintf (mp->ps->t1_line_array, fmt, args);
2119 t1_puts (mp,mp->ps->t1_line_array);
2123 static void t1_init_params (MP mp, char *open_name_prefix,
2124 char *cur_file_name) {
2125 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2126 t1_log (open_name_prefix);
2127 t1_log (cur_file_name);
2129 mp->ps->t1_lenIV = 4;
2130 mp->ps->t1_dr = 55665;
2131 mp->ps->t1_er = 55665;
2132 mp->ps->t1_in_eexec = 0;
2133 mp->ps->t1_cs = false;
2134 mp->ps->t1_scan = true;
2135 mp->ps->t1_synthetic = false;
2136 mp->ps->t1_eexec_encrypt = false;
2137 mp->ps->t1_block_length = 0;
2140 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2141 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2142 t1_log (close_name_suffix);
2147 static void t1_check_block_len (MP mp, boolean decrypt) {
2150 if (mp->ps->t1_block_length == 0)
2152 c = t1_getbyte (mp);
2154 c = edecrypt (mp,c);
2155 l = mp->ps->t1_block_length;
2156 if (!(l == 0 && (c == 10 || c == 13))) {
2157 snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2163 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2165 if (!mp->ps->t1_pfa)
2166 t1_check_block_len (mp, false);
2167 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2168 edecrypt (mp, t1_getbyte (mp));
2169 *mp->ps->t1_line_ptr++ = 0;
2171 mp->ps->t1_eexec_encrypt = true;
2172 if (!mp->ps->read_encoding_only)
2173 if (is_included (fm_cur))
2174 t1_putline (mp); /* to put the first four bytes */
2176 static void t1_stop_eexec (MP mp) {
2178 end_last_eexec_line ();
2179 if (!mp->ps->t1_pfa)
2180 t1_check_block_len (mp,true);
2182 c = edecrypt (mp, t1_getbyte (mp));
2183 if (!(c == 10 || c == 13)) {
2184 if (mp->ps->last_hexbyte == 0)
2187 mp_warn (mp,"unexpected data after eexec");
2190 mp->ps->t1_cs = false;
2191 mp->ps->t1_in_eexec = 2;
2193 static void t1_modify_fm (MP mp) {
2194 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2197 static void t1_modify_italic (MP mp) {
2198 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2203 const char *pdfname;
2213 static key_entry font_keys[FONT_KEYS_NUM] = {
2214 {"Ascent", "Ascender", 0, false},
2215 {"CapHeight", "CapHeight", 0, false},
2216 {"Descent", "Descender", 0, false},
2217 {"FontName", "FontName", 0, false},
2218 {"ItalicAngle", "ItalicAngle", 0, false},
2219 {"StemV", "StdVW", 0, false},
2220 {"XHeight", "XHeight", 0, false},
2221 {"FontBBox", "FontBBox", 0, false},
2233 @d ITALIC_ANGLE_CODE 4
2239 @d FONTBBOX4_CODE 10
2240 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2243 static void t1_scan_keys (MP mp, int tex_font,fm_entry *fm_cur) {
2247 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2248 if (t1_prefix ("/FontMatrix")) {
2252 if (t1_prefix ("/ItalicAngle")) {
2253 t1_modify_italic (mp);
2257 if (t1_prefix ("/FontType")) {
2258 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2259 if ((i = t1_scan_num (mp,p, 0)) != 1) {
2261 snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2262 mp_fatal_error(mp,s);
2266 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2267 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2269 if (key - font_keys == MAX_KEY_CODE)
2272 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2274 if ((k = key - font_keys) == FONTNAME_CODE) {
2277 remove_eol (p, mp->ps->t1_line_array);
2278 snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2279 mp_fatal_error(mp,s);
2281 r = ++p; /* skip the slash */
2282 if (is_included (fm_cur)) {
2283 /* save the fontname */
2284 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2285 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2286 mp->ps->fontname_buf[i]=0;
2288 if(is_subsetted (fm_cur)) {
2289 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2290 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2292 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2294 alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
2296 strncpy (r, fm_cur->subset_tag , 6);
2298 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2299 mp->ps->t1_line_ptr = eol (r);
2301 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2303 mp->ps->t1_line_ptr = eol (r);
2308 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2309 && (*p == '[' || *p == '{'))
2311 if (k == FONTBBOX1_CODE) {
2312 for (i = 0; i < 4; i++) {
2313 key[i].value = t1_scan_num (mp, p, &r);
2318 key->value = t1_scan_num (mp, p, 0);
2320 static void t1_scan_param (MP mp, int tex_font,fm_entry *fm_cur)
2322 static const char *lenIV = "/lenIV";
2323 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2325 if (t1_prefix (lenIV)) {
2326 mp->ps->t1_lenIV = t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2329 t1_scan_keys (mp, tex_font,fm_cur);
2331 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2332 if (glyph_names[b] != notdef) {
2333 mp_xfree (glyph_names[b]);
2334 glyph_names[b] = (char *) notdef;
2336 if (glyph_names[a] != notdef) {
2337 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2340 static void t1_builtin_enc (MP mp) {
2341 int i, a, b, c, counter = 0;
2344 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2346 if (t1_suffix ("def")) { /* predefined encoding */
2347 sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2348 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2349 for (i = 0; i < 256; i++)
2350 if (standard_glyph_names[i] == notdef)
2351 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2353 mp->ps->t1_builtin_glyph_names[i] =
2354 mp_xstrdup (mp,standard_glyph_names[i]);
2355 mp->ps->t1_encoding = ENC_STANDARD;
2358 snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2359 mp->ps->t1_buf_array);
2360 mp_fatal_error(mp,s);
2364 mp->ps->t1_encoding = ENC_BUILTIN;
2366 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2367 * not a predefined encoding
2369 * We have two possible forms of Encoding vector. The first case is
2371 * /Encoding [/a /b /c...] readonly def
2373 * and the second case can look like
2375 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2381 for (i = 0; i < 256; i++)
2382 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2383 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2384 r = strchr (mp->ps->t1_line_array, '[') + 1;
2388 for (p = mp->ps->t1_buf_array, r++;
2389 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2393 if (counter > 255) {
2395 (mp, "encoding vector contains more than 256 names");
2397 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2398 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2401 if (*r != 10 && *r != '%') {
2402 if (str_prefix (r, "] def")
2403 || str_prefix (r, "] readonly def"))
2407 remove_eol (r, mp->ps->t1_line_array);
2408 snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2409 mp->ps->t1_line_array);
2410 mp_fatal_error(mp,s);
2414 r = mp->ps->t1_line_array;
2416 } else { /* the second case */
2417 p = strchr (mp->ps->t1_line_array, 10);
2421 p = mp->ps->t1_line_array;
2424 check for `dup <index> <glyph> put'
2426 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2427 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2428 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2429 mp->ps->t1_builtin_glyph_names[i] =
2430 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2431 p = strstr (p, " put") + strlen (" put");
2435 check for `dup dup <to> exch <from> get put'
2437 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2438 && valid_code (a) && valid_code (b)) {
2439 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2440 p = strstr (p, " get put") + strlen (" get put");
2444 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2447 (p, "dup dup %i %i getinterval %i exch putinterval",
2448 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2449 && valid_code (c)) {
2450 for (i = 0; i < c; i++)
2451 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2452 p = strstr (p, " putinterval") + strlen (" putinterval");
2456 check for `def' or `readonly def'
2458 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2459 && strcmp (p, "def\n") == 0)
2462 skip an unrecognizable word
2465 while (*p != ' ' && *p != 10)
2473 static void t1_check_end (MP mp) {
2477 if (t1_prefix ("{restore}"))
2483 char *ff_name; /* base name of font file */
2484 char *ff_path; /* full path to font file */
2488 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2490 ff = check_ff_exist (mp, fm_cur);
2491 if (ff->ff_path != NULL) {
2492 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "rb", mp_filetype_font);
2494 mp_warn (mp, "cannot open Type 1 font file for reading");
2497 t1_init_params (mp,(char *)open_name_prefix,fm_cur->ff_name);
2498 mp->ps->fontfile_found = true;
2502 static void t1_scan_only (MP mp, int tex_font, fm_entry *fm_cur) {
2505 t1_scan_param (mp,tex_font, fm_cur);
2507 while (mp->ps->t1_in_eexec == 0);
2508 t1_start_eexec (mp,fm_cur);
2511 t1_scan_param (mp,tex_font, fm_cur);
2513 while (!(t1_charstrings () || t1_subrs ()));
2516 static void t1_include (MP mp, int tex_font, fm_entry *fm_cur) {
2519 t1_scan_param (mp,tex_font, fm_cur);
2522 while (mp->ps->t1_in_eexec == 0);
2523 t1_start_eexec (mp,fm_cur);
2526 t1_scan_param (mp,tex_font, fm_cur);
2529 while (!(t1_charstrings () || t1_subrs ()));
2530 mp->ps->t1_cs = true;
2535 while (!t1_end_eexec ());
2537 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2542 while (!t1_cleartomark ());
2543 t1_check_end (mp); /* write "{restore}if" if found */
2548 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2550 snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2551 mp_fatal_error(mp,s);
2555 static const char **check_cs_token_pair (MP mp) {
2556 const char **p = (const char **) cs_token_pairs_list;
2557 for (; p[0] != NULL; ++p)
2558 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2563 static void cs_store (MP mp, boolean is_subr) {
2567 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2568 *mp->ps->t1_buf_ptr++ = *p++);
2569 *mp->ps->t1_buf_ptr = 0;
2571 subr = t1_scan_num (mp, p + 1, 0);
2573 ptr = mp->ps->subr_tab + subr;
2575 ptr = mp->ps->cs_ptr++;
2576 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2578 snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2579 mp_fatal_error(mp,s);
2581 if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
2582 ptr->glyph_name = (char *) notdef;
2584 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2586 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2587 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2588 (unsigned) (mp->ps->t1_cslen + 4));
2589 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2590 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2591 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2592 *mp->ps->t1_buf_ptr++ = 10;
2593 if (is_subr && mp->ps->cs_token_pair == NULL)
2594 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2595 ptr->len = mp->ps->t1_buf_ptr - mp->ps->t1_buf_array;
2596 ptr->cslen = mp->ps->t1_cslen;
2597 ptr->data = mp_xmalloc (mp,ptr->len , sizeof (byte));
2598 memcpy (ptr->data, mp->ps->t1_buf_array, ptr->len);
2602 #define store_subr(mp) cs_store(mp,true)
2603 #define store_cs(mp) cs_store(mp,false)
2605 #define CC_STACK_SIZE 24
2607 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2608 static cc_entry cc_tab[CS_MAX];
2609 static boolean is_cc_init = false;
2613 if (stack_ptr - cc_stack < (N)) \
2617 #define stack_error(N) { \
2619 snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2620 (int) N, (int)(stack_ptr - cc_stack)); \
2626 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2628 #define cc_push(V) *stack_ptr++ = V
2629 #define cc_clear() stack_ptr = cc_stack
2631 #define set_cc(N, B, A, C) \
2632 cc_tab[N].nargs = A; \
2633 cc_tab[N].bottom = B; \
2634 cc_tab[N].clear = C; \
2635 cc_tab[N].valid = true
2637 static void cc_init (void) {
2641 for (i = 0; i < CS_MAX; i++)
2642 cc_tab[i].valid = false;
2643 set_cc (CS_HSTEM, true, 2, true);
2644 set_cc (CS_VSTEM, true, 2, true);
2645 set_cc (CS_VMOVETO, true, 1, true);
2646 set_cc (CS_RLINETO, true, 2, true);
2647 set_cc (CS_HLINETO, true, 1, true);
2648 set_cc (CS_VLINETO, true, 1, true);
2649 set_cc (CS_RRCURVETO, true, 6, true);
2650 set_cc (CS_CLOSEPATH, false, 0, true);
2651 set_cc (CS_CALLSUBR, false, 1, false);
2652 set_cc (CS_RETURN, false, 0, false);
2654 |set_cc(CS_ESCAPE, false, 0, false);|
2656 set_cc (CS_HSBW, true, 2, true);
2657 set_cc (CS_ENDCHAR, false, 0, true);
2658 set_cc (CS_RMOVETO, true, 2, true);
2659 set_cc (CS_HMOVETO, true, 1, true);
2660 set_cc (CS_VHCURVETO, true, 4, true);
2661 set_cc (CS_HVCURVETO, true, 4, true);
2662 set_cc (CS_DOTSECTION, false, 0, true);
2663 set_cc (CS_VSTEM3, true, 6, true);
2664 set_cc (CS_HSTEM3, true, 6, true);
2665 set_cc (CS_SEAC, true, 5, true);
2666 set_cc (CS_SBW, true, 4, true);
2667 set_cc (CS_DIV, false, 2, false);
2668 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2669 set_cc (CS_POP, false, 0, false);
2670 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2676 @d cs_getchar(mp) cdecrypt(mp,*data++, &cr)
2678 @d mark_subr(mp,n) cs_mark(mp,0, n)
2679 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2680 @d SMALL_BUF_SIZE 256
2683 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2684 char buf[SMALL_BUF_SIZE];
2687 va_start (args, fmt);
2688 vsprintf (buf, fmt, args);
2690 if (cs_name == NULL) {
2691 snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2693 snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2698 static void cs_mark (MP mp, const char *cs_name, int subr)
2704 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2708 if (cs_name == NULL) {
2710 ptr = mp->ps->subr_tab + subr;
2714 if (mp->ps->cs_notdef != NULL &&
2715 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2716 ptr = mp->ps->cs_notdef;
2718 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2719 if (strcmp (ptr->glyph_name, cs_name) == 0)
2721 if (ptr == mp->ps->cs_ptr) {
2723 snprintf (s,128,"glyph `%s' undefined", cs_name);
2727 if (ptr->glyph_name == notdef)
2728 mp->ps->cs_notdef = ptr;
2731 /* only marked CharString entries and invalid entries can be skipped;
2732 valid marked subrs must be parsed to keep the stack in sync */
2733 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2735 ptr->is_used = true;
2737 cs_len = ptr->cslen;
2738 data = ptr->data + 4;
2739 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2741 while (cs_len > 0) {
2743 b = cs_getchar (mp);
2747 else if (b <= 250) {
2749 a = ((b - 247) << 8) + 108 + cs_getchar (mp);
2750 } else if (b <= 254) {
2752 a = -((b - 251) << 8) - 108 - cs_getchar (mp);
2755 a = (cs_getchar (mp) & 0xff) << 24;
2756 a |= (cs_getchar (mp) & 0xff) << 16;
2757 a |= (cs_getchar (mp) & 0xff) << 8;
2758 a |= (cs_getchar (mp) & 0xff) << 0;
2759 if (sizeof (integer) > 4 && (a & 0x80000000))
2764 if (b == CS_ESCAPE) {
2765 b = cs_getchar (mp) + CS_1BYTE_MAX;
2769 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2775 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2779 if (stack_ptr - cc_stack < cc->nargs)
2780 cs_warn (mp,cs_name, subr,
2781 "less arguments on stack (%i) than required (%i)",
2782 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2783 else if (stack_ptr - cc_stack > cc->nargs)
2784 cs_warn (mp,cs_name, subr,
2785 "more arguments on stack (%i) than required (%i)",
2786 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2788 switch (cc - cc_tab) {
2793 if (!mp->ps->subr_tab[a1].valid) {
2794 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2802 case CS_CALLOTHERSUBR:
2803 if (cc_get (-1) == 3)
2804 lastargOtherSubr3 = cc_get (-3);
2805 a1 = cc_get (-2) + 2;
2809 cc_push (lastargOtherSubr3);
2810 /* the only case when we care about the value being pushed onto
2811 stack is when POP follows CALLOTHERSUBR (changing hints by
2819 mark_cs (mp,standard_glyph_names[a1]);
2820 mark_cs (mp,standard_glyph_names[a2]);
2829 cs_error: /* an error occured during parsing */
2832 ptr->is_used = false;
2835 static void t1_subset_ascii_part (MP mp, int tex_font, fm_entry *fm_cur)
2839 while (!t1_prefix ("/Encoding")) {
2840 t1_scan_param (mp,tex_font, fm_cur);
2844 t1_builtin_enc (mp);
2845 if (is_reencoded (fm_cur))
2846 mp->ps->t1_glyph_names = external_enc ();
2848 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2850 |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2851 make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2852 update_subset_tag ();
2855 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2856 t1_puts (mp,"/Encoding StandardEncoding def\n");
2859 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2860 for (i = 0, j = 0; i < 256; i++) {
2861 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2863 t1_printf (mp,"dup %i /%s put\n", (int) t1_char (i),
2864 mp->ps->t1_glyph_names[i]);
2867 /* We didn't mark anything for the Encoding array. */
2868 /* We add "dup 0 /.notdef put" for compatibility */
2869 /* with Acrobat 5.0. */
2871 t1_puts (mp,"dup 0 /.notdef put\n");
2872 t1_puts (mp,"readonly def\n");
2876 t1_scan_param (mp,tex_font, fm_cur);
2877 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
2880 while (mp->ps->t1_in_eexec == 0);
2883 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
2884 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
2886 static void cs_init (MP mp) {
2887 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2888 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2889 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2890 mp->ps->cs_token_pair = NULL;
2891 mp->ps->subr_tab = NULL;
2892 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2893 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2896 static void init_cs_entry ( cs_entry * cs) {
2898 cs->glyph_name = NULL;
2901 cs->is_used = false;
2905 static void t1_mark_glyphs (MP mp, int tex_font);
2907 static void t1_read_subrs (MP mp, int tex_font, fm_entry *fm_cur)
2912 while (!(t1_charstrings () || t1_subrs ())) {
2913 t1_scan_param (mp,tex_font, fm_cur);
2918 mp->ps->t1_cs = true;
2919 mp->ps->t1_scan = false;
2922 mp->ps->subr_size_pos = strlen ("/Subrs") + 1;
2923 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2924 mp->ps->subr_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2925 if (mp->ps->subr_size == 0) {
2926 while (!t1_charstrings ())
2930 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
2931 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,mp->ps->subr_size, sizeof (cs_entry));
2932 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2933 init_cs_entry (ptr);
2934 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2936 while (mp->ps->t1_cslen) {
2940 /* mark the first four entries without parsing */
2941 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2942 mp->ps->subr_tab[i].is_used = true;
2943 /* the end of the Subrs array might have more than one line so we need to
2944 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2945 the Subrs array followed by the CharStrings dict immediately (synthetic
2946 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2947 we will treat the font as synthetic and ignore everything until next
2950 #define POST_SUBRS_SCAN 5
2952 *mp->ps->t1_buf_array = 0;
2953 for (i = 0; i < POST_SUBRS_SCAN; i++) {
2954 if (t1_charstrings ())
2956 s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2957 alloc_array (t1_buf, s, T1_BUF_SIZE);
2958 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2961 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2962 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2963 suppose synthetic font */
2964 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2966 mp_xfree (ptr->data);
2967 mp_xfree (mp->ps->subr_tab);
2968 mp_xfree (mp->ps->subr_array_start);
2969 mp_xfree (mp->ps->subr_array_end);
2971 mp->ps->t1_cs = false;
2972 mp->ps->t1_synthetic = true;
2973 while (!(t1_charstrings () || t1_subrs ()))
2980 static void t1_flush_cs (MP mp, boolean is_subr)
2983 byte *r, *return_cs = NULL;
2984 cs_entry *tab, *end_tab, *ptr;
2985 char *start_line, *line_end;
2986 int count, size_pos;
2987 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2989 start_line = mp->ps->subr_array_start;
2990 line_end = mp->ps->subr_array_end;
2991 size_pos = mp->ps->subr_size_pos;
2992 tab = mp->ps->subr_tab;
2993 count = mp->ps->subr_max + 1;
2994 end_tab = mp->ps->subr_tab + count;
2996 start_line = mp->ps->cs_dict_start;
2997 line_end = mp->ps->cs_dict_end;
2998 size_pos = mp->ps->cs_size_pos;
2999 tab = mp->ps->cs_tab;
3000 end_tab = mp->ps->cs_ptr;
3001 count = mp->ps->cs_count;
3003 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
3004 for (p = start_line; p - start_line < size_pos;)
3005 *mp->ps->t1_line_ptr++ = *p++;
3006 while (isdigit (*p))
3008 sprintf (mp->ps->t1_line_ptr, "%u", count);
3009 strcat (mp->ps->t1_line_ptr, p);
3010 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3013 /* create |return_cs| to replace unsused subr's */
3017 return_cs = mp_xmalloc (mp, (mp->ps->t1_lenIV + 1) , sizeof(byte));
3018 if ( mp->ps->t1_lenIV > 0) {
3019 for (cs_len = 0, r = return_cs; cs_len < mp->ps->t1_lenIV; cs_len++, r++)
3020 *r = cencrypt (mp,0x00, &cr);
3021 *r = cencrypt (mp,CS_RETURN, &cr);
3023 *return_cs = CS_RETURN;
3028 for (ptr = tab; ptr < end_tab; ptr++) {
3031 sprintf (mp->ps->t1_line_array, "dup %i %u", (int) (ptr - tab),
3034 sprintf (mp->ps->t1_line_array, "/%s %u", ptr->glyph_name, ptr->cslen);
3035 p = strend (mp->ps->t1_line_array);
3036 memcpy (p, ptr->data, ptr->len);
3037 mp->ps->t1_line_ptr = p + ptr->len;
3040 /* replace unsused subr's by |return_cs| */
3042 sprintf (mp->ps->t1_line_array, "dup %i %u%s ", (int) (ptr - tab),
3043 cs_len, mp->ps->cs_token_pair[0]);
3044 p = strend (mp->ps->t1_line_array);
3045 memcpy (p, return_cs, cs_len);
3046 mp->ps->t1_line_ptr = p + cs_len;
3048 sprintf (mp->ps->t1_line_array, " %s", mp->ps->cs_token_pair[1]);
3049 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3053 mp_xfree (ptr->data);
3054 if (ptr->glyph_name != notdef)
3055 mp_xfree (ptr->glyph_name);
3057 sprintf (mp->ps->t1_line_array, "%s", line_end);
3058 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3061 mp_xfree (return_cs);
3063 mp_xfree (start_line);
3064 mp_xfree (line_end);
3067 static void t1_mark_glyphs (MP mp, int tex_font)
3070 char *charset = extra_charset ();
3073 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
3074 if (mp->ps->cs_tab != NULL)
3075 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3077 ptr->is_used = true;
3078 if (mp->ps->subr_tab != NULL) {
3079 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3081 ptr->is_used = true;
3082 mp->ps->subr_max = mp->ps->subr_size - 1;
3086 mark_cs (mp,notdef);
3087 for (i = 0; i < 256; i++)
3088 if (is_used_char (i)) {
3089 if (mp->ps->t1_glyph_names[i] == notdef) {
3091 snprintf(s,128, "character %i is mapped to %s", i, notdef);
3094 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3096 if (charset == NULL)
3098 g = s = charset + 1; /* skip the first '/' */
3101 while (*s != '/' && s < r)
3103 *s = 0; /* terminate g by rewriting '/' to 0 */
3108 if (mp->ps->subr_tab != NULL)
3109 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3110 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3112 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3113 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3116 static void t1_subset_charstrings (MP mp, int tex_font)
3119 mp->ps->cs_size_pos =
3120 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3121 - mp->ps->t1_line_array + 1;
3122 /* |cs_size_pos| points to the number indicating
3123 dict size after "/CharStrings" */
3124 mp->ps->cs_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3125 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,mp->ps->cs_size, sizeof(cs_entry));
3126 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3127 init_cs_entry (ptr);
3128 mp->ps->cs_notdef = NULL;
3129 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3131 while (mp->ps->t1_cslen) {
3135 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3136 t1_mark_glyphs (mp,tex_font);
3137 if (mp->ps->subr_tab != NULL) {
3138 if (mp->ps->cs_token_pair == NULL)
3140 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3143 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3149 static void t1_subset_end (MP mp)
3151 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3152 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3156 while (!t1_end_eexec ())
3157 t1_getline (mp); /* ignore the rest */
3158 t1_putline (mp); /* write "mark currentfile closefile" */
3160 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3165 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3166 while (!t1_cleartomark ()) {
3170 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3171 t1_check_end (mp); /* write "{restore}if" if found */
3175 static int t1_updatefm (MP mp, int f, fm_entry *fm)
3178 mp->ps->read_encoding_only = true;
3179 if (!t1_open_fontfile (mp,fm,NULL)) {
3182 t1_scan_only (mp,f, fm);
3183 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3185 while (*p != ' ' && *p != 0)
3189 t1_close_font_file (mp,"");
3194 static void writet1 (MP mp, int tex_font, fm_entry *fm_cur) {
3195 int save_selector = mp->selector;
3196 mp_normalize_selector(mp);
3197 mp->ps->read_encoding_only = false;
3198 if (!is_included (fm_cur)) { /* scan parameters from font file */
3199 if (!t1_open_fontfile (mp,fm_cur,"{"))
3201 t1_scan_only (mp,tex_font, fm_cur);
3202 t1_close_font_file (mp,"}");
3205 if (!is_subsetted (fm_cur)) { /* include entire font */
3206 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3208 t1_include (mp,tex_font,fm_cur);
3209 t1_close_font_file (mp,">>");
3212 /* partial downloading */
3213 if (!t1_open_fontfile (mp,fm_cur,"<"))
3215 t1_subset_ascii_part (mp,tex_font,fm_cur);
3216 t1_start_eexec (mp,fm_cur);
3219 t1_read_subrs (mp,tex_font, fm_cur);
3220 t1_subset_charstrings (mp,tex_font);
3222 t1_close_font_file (mp,">");
3223 mp->selector = save_selector;
3227 static void t1_free (MP mp);
3230 static void t1_free (MP mp) {
3231 mp_xfree (mp->ps->t1_line_array);
3232 mp_xfree (mp->ps->t1_buf_array);
3236 @* \[44d] Embedding fonts.
3238 @ The |tfm_num| is officially of type |font_number|, but that
3239 type does not exist yet at this point in the output order.
3243 char *tfm_name; /* TFM file name */
3244 char *ps_name; /* PostScript name */
3245 integer flags; /* font flags */
3246 char *ff_name; /* font file name */
3247 char *subset_tag; /* pseudoUniqueTag for subsetted font */
3248 enc_entry *encoding; /* pointer to corresponding encoding */
3249 unsigned int tfm_num; /* number of the TFM refering this entry */
3250 unsigned short type; /* font type (T1/TTF/...) */
3251 short slant; /* SlantFont */
3252 short extend; /* ExtendFont */
3253 integer ff_objnum; /* FontFile object number */
3254 integer fn_objnum; /* FontName/BaseName object number */
3255 integer fd_objnum; /* FontDescriptor object number */
3256 char *charset; /* string containing used glyphs */
3257 boolean all_glyphs; /* embed all glyphs? */
3258 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
3259 short tfm_avail; /* flags whether a tfm is available */
3260 short pid; /* Pid for truetype fonts */
3261 short eid; /* Eid for truetype fonts */
3267 #define FONTNAME_BUF_SIZE 128
3268 boolean fontfile_found;
3269 boolean is_otf_font;
3270 char fontname_buf[FONTNAME_BUF_SIZE];
3278 @d set_included(fm) ((fm)->type |= F_INCLUDED)
3279 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
3280 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
3281 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
3283 @d is_included(fm) ((fm)->type & F_INCLUDED)
3284 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
3285 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
3286 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
3287 @d is_reencoded(fm) ((fm)->encoding != NULL)
3288 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
3289 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
3291 @d fm_slant(fm) (fm)->slant
3292 @d fm_extend(fm) (fm)->extend
3293 @d fm_fontfile(fm) (fm)->ff_name
3296 static boolean mp_font_is_reencoded (MP mp, int f);
3297 static boolean mp_font_is_included (MP mp, int f);
3298 static boolean mp_font_is_subsetted (MP mp, int f);
3301 static boolean mp_font_is_reencoded (MP mp, int f) {
3303 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3305 && (fm->ps_name != NULL)
3306 && is_reencoded (fm))
3311 static boolean mp_font_is_included (MP mp, int f) {
3313 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3315 && (fm->ps_name != NULL && fm->ff_name != NULL)
3316 && is_included (fm))
3321 static boolean mp_font_is_subsetted (MP mp, int f) {
3323 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
3325 && (fm->ps_name != NULL && fm->ff_name != NULL)
3326 && is_included (fm) && is_subsetted (fm))
3332 @ @<Exported function headers@>=
3333 char * mp_fm_encoding_name (MP mp, int f);
3334 char * mp_fm_font_name (MP mp, int f);
3337 static char * mp_fm_font_subset_name (MP mp, int f);
3340 @c char * mp_fm_encoding_name (MP mp, int f) {
3343 if (mp_has_fm_entry (mp, f, &fm)) {
3344 if (fm != NULL && (fm->ps_name != NULL)) {
3345 if (is_reencoded (fm)) {
3347 if (e->enc_name!=NULL)
3348 return mp_xstrdup(mp,e->enc_name);
3354 print_err ("fontmap encoding problems for font ");
3355 mp_print(mp,mp->font_name[f]);
3359 char * mp_fm_font_name (MP mp, int f) {
3361 if (mp_has_fm_entry (mp, f,&fm)) {
3362 if (fm != NULL && (fm->ps_name != NULL)) {
3363 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3364 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3365 if (t1_updatefm(mp,f,fm)) {
3366 mp->font_ps_name_fixed[f] = true;
3368 print_err ("font loading problems for font ");
3369 mp_print(mp,mp->font_name[f]);
3373 return mp_xstrdup(mp,fm->ps_name);
3376 print_err ("fontmap name problems for font ");
3377 mp_print(mp,mp->font_name[f]);
3382 static char * mp_fm_font_subset_name (MP mp, int f) {
3384 if (mp_has_fm_entry (mp, f, &fm)) {
3385 if (fm != NULL && (fm->ps_name != NULL)) {
3386 if (is_subsetted(fm)) {
3387 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3388 snprintf(s,strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3391 return mp_xstrdup(mp,fm->ps_name);
3395 print_err ("fontmap name problems for font ");
3396 mp_print(mp,mp->font_name[f]);
3402 static integer mp_fm_font_slant (MP mp, int f);
3403 static integer mp_fm_font_extend (MP mp, int f);
3406 @c static integer mp_fm_font_slant (MP mp, int f) {
3408 if (mp_has_fm_entry (mp, f, &fm)) {
3409 if (fm != NULL && (fm->ps_name != NULL)) {
3415 static integer mp_fm_font_extend (MP mp, int f) {
3417 if (mp_has_fm_entry (mp, f, &fm)) {
3418 if (fm != NULL && (fm->ps_name != NULL)) {
3426 static boolean mp_do_ps_font (MP mp, font_number f);
3428 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3430 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3433 if (is_truetype(fm_cur) ||
3434 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3437 if (is_included(fm_cur)) {
3438 mp_ps_print_nl(mp,"%%BeginResource: font ");
3439 if (is_subsetted(fm_cur)) {
3440 mp_ps_print(mp, fm_cur->subset_tag);
3441 mp_ps_print_char(mp,'-');
3443 mp_ps_print(mp, fm_cur->ps_name);
3445 writet1 (mp,f,fm_cur);
3446 mp_ps_print_nl(mp,"%%EndResource");
3452 @ Included subset fonts do not need and encoding vector, make
3453 sure we skip that case.
3456 static void mp_list_used_resources (MP mp, int prologues, int procset);
3458 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
3459 font_number f; /* fonts used in a text node or as loop counters */
3460 int ff; /* a loop counter */
3461 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3464 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
3466 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3469 for (f=null_font+1;f<=mp->last_fnum;f++) {
3470 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3471 for (ff=ldf;ff>=null_font;ff--) {
3472 if ( mp_has_font_size(mp,ff) )
3473 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3476 if ( mp_font_is_subsetted(mp,f) )
3478 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
3479 (unsigned)mp->max_print_line )
3480 mp_ps_print_nl(mp, "%%+ encoding");
3483 mp_ps_print_nl(mp, "%%+ encoding");
3485 mp_ps_print_char(mp, ' ');
3486 mp_ps_print(mp, mp->font_enc_name[f]);
3494 for (f=null_font+1;f<=mp->last_fnum;f++) {
3495 if ( mp_has_font_size(mp,f) ) {
3496 for (ff=ldf;ff>=null_font;ff--) {
3497 if ( mp_has_font_size(mp,ff) )
3498 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3501 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
3502 (unsigned)mp->max_print_line )
3503 mp_ps_print_nl(mp, "%%+ font");
3506 mp_ps_print_nl(mp, "%%+ font");
3508 mp_ps_print_char(mp, ' ');
3509 if ( (prologues==3)&&
3510 (mp_font_is_subsetted(mp,f)) )
3511 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3513 mp_ps_print(mp, mp->font_ps_name[f]);
3523 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
3525 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
3526 font_number f; /* fonts used in a text node or as loop counters */
3527 int ff; /* a loop counter */
3528 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3531 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3533 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3536 for (f=null_font+1;f<=mp->last_fnum;f++) {
3537 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3538 for (ff=ldf;ff>= null_font;ff++) {
3539 if ( mp_has_font_size(mp,ff) )
3540 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3543 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3545 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(unsigned)mp->max_print_line )
3546 mp_ps_print_nl(mp, "%%+ encoding");
3549 mp_ps_print_nl(mp, "%%+ encoding");
3551 mp_ps_print_char(mp, ' ');
3552 mp_ps_print(mp, mp->font_enc_name[f]);
3561 for (f=null_font+1;f<=mp->last_fnum;f++) {
3562 if ( mp_has_font_size(mp,f) ) {
3563 for (ff=ldf;ff>= null_font;ff--) {
3564 if ( mp_has_font_size(mp,ff) )
3565 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3568 if ( ! mp_font_is_included(mp,f) )
3570 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3571 mp_ps_print_nl(mp, "%%+ font");
3574 mp_ps_print_nl(mp, "%%+ font");
3576 mp_ps_print_char(mp, ' ');
3577 if ( mp_font_is_subsetted(mp,f) )
3578 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3580 mp_ps_print(mp, mp->font_ps_name[f]);
3590 @ @<Declarations...@>=
3591 static void mp_list_needed_resources (MP mp, int prologues);
3593 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
3594 font_number f; /* fonts used in a text node or as loop counters */
3595 int ff; /* a loop counter */
3596 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3600 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3601 if ( mp_has_font_size(mp,f)) {
3602 for (ff=ldf;ff>=null_font;ff--) {
3603 if ( mp_has_font_size(mp,ff) )
3604 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3607 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3609 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3610 mp_ps_print_nl(mp, "%%+ font");
3613 mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
3615 mp_ps_print_char(mp, ' ');
3616 mp_ps_print(mp, mp->font_ps_name[f]);
3622 if ( ! firstitem ) {
3626 for (f=null_font+1;f<= mp->last_fnum;f++) {
3627 if ( mp_has_font_size(mp,f) ) {
3628 for (ff=ldf;ff>=null_font;ff-- ) {
3629 if ( mp_has_font_size(mp,ff) )
3630 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3633 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3635 mp_ps_print(mp, "%%IncludeResource: font ");
3636 mp_ps_print(mp, mp->font_ps_name[f]);
3647 static void mp_write_font_definition (MP mp, font_number f, int prologues);
3651 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3652 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3654 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
3655 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3656 (mp_fm_font_extend(mp,f)!=0)||
3657 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3658 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3659 if ( (mp_font_is_subsetted(mp,f))&&
3660 (mp_font_is_included(mp,f))&&(prologues==3))
3661 mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3663 mp_ps_name_out(mp, mp->font_ps_name[f],true);
3664 mp_ps_print(mp, " fcp");
3666 if ( applied_reencoding(f) ) {
3667 mp_ps_print(mp, "/Encoding ");
3668 mp_ps_print(mp, mp->font_enc_name[f]);
3669 mp_ps_print(mp, " def ");
3671 if ( mp_fm_font_slant(mp,f)!=0 ) {
3672 mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
3673 mp_ps_print(mp, " SlantFont ");
3675 if ( mp_fm_font_extend(mp,f)!=0 ) {
3676 mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3677 mp_ps_print(mp, " ExtendFont ");
3679 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3680 mp_ps_print(mp, " 890 ScaleFont ");
3681 mp_ps_print(mp, " 277 SlantFont ");
3683 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3684 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3685 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3686 mp_ps_print(mp, "/space[0 -278]def ");
3687 mp_ps_print(mp, "/a12[-904 -939]def ");
3688 mp_ps_print(mp, "end def ");
3690 mp_ps_print(mp, "currentdict end");
3692 mp_ps_print_defined_name(mp,f,prologues);
3693 mp_ps_print(mp, " exch definefont pop");
3699 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
3702 @c static void mp_ps_print_defined_name(MP mp, font_number A, int prologues) {
3703 mp_ps_print(mp, " /");
3704 if ((mp_font_is_subsetted(mp,(A)))&&
3705 (mp_font_is_included(mp,(A)))&&(prologues==3))
3706 mp_ps_print(mp, mp_fm_font_subset_name(mp,(A)));
3708 mp_ps_print(mp, mp->font_ps_name[(A)]);
3709 if ( mp_xstrcmp(mp->font_name[(A)],"psyrgo")==0 )
3710 mp_ps_print(mp, "-Slanted");
3711 if ( mp_xstrcmp(mp->font_name[(A)],"zpzdr-reversed")==0 )
3712 mp_ps_print(mp, "-Reverse");
3713 if ( applied_reencoding((A)) ) {
3714 mp_ps_print(mp, "-");
3715 mp_ps_print(mp, mp->font_enc_name[(A)]);
3717 if ( mp_fm_font_slant(mp,(A))!=0 ) {
3718 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,(A))) ;
3720 if ( mp_fm_font_extend(mp,(A))!=0 ) {
3721 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,(A)));
3725 @ @<Include encodings and fonts for edge structure~|h|@>=
3726 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
3727 @<Embed fonts that are available@>
3729 @ @<Embed fonts that are available@>=
3732 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3735 for (f=null_font+1;f<=mp->last_fnum;f++) {
3736 if ( cur_fsize[f]!=null ) {
3737 if (prologues==3 ) {
3738 if ( ! mp_do_ps_font(mp,f) ) {
3739 if ( mp_has_fm_entry(mp,f, NULL) ) {
3740 print_err("Font embedding failed");
3745 cur_fsize[f]=link(cur_fsize[f]);
3746 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3750 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3752 } while (! done_fonts);
3755 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3758 mp_apply_mark_string_chars(mp, h, next_size);
3761 @ We also need to keep track of which characters are used in text nodes
3762 in the edge structure that is being shipped out. This is done by procedures
3763 that use the left-over |b3| field in the |char_info| words; i.e.,
3764 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3767 enum mp_char_mark_state {mp_unused=0, mp_used};
3770 void mp_mark_string_chars (MP mp,font_number f, char *s) ;
3773 void mp_mark_string_chars (MP mp,font_number f, char *s) {
3774 integer b; /* |char_base[f]| */
3775 ASCII_code bc,ec; /* only characters between these bounds are marked */
3776 char *k; /* an index into string |s| */
3782 if ( (*k>=bc)&&(*k<=ec) )
3783 mp->font_info[b+*k].qqqq.b3=mp_used;
3790 void mp_unmark_font (MP mp,font_number f) ;
3793 void mp_unmark_font (MP mp,font_number f) {
3794 int k; /* an index into |font_info| */
3795 for (k= mp->char_base[f]+mp->font_bc[f];
3796 k<=mp->char_base[f]+mp->font_ec[f];
3798 mp->font_info[k].qqqq.b3=mp_unused;
3803 void mp_print_improved_prologue (MP mp, struct mp_edge_object *h, int prologues, int procset) ;
3806 void mp_print_improved_prologue (MP mp, struct mp_edge_object *h, int prologues, int procset) {
3807 quarterword next_size; /* the size index for fonts being listed */
3808 pointer *cur_fsize; /* current positions in |font_sizes| */
3809 boolean done_fonts; /* have we finished listing the fonts in the header? */
3810 font_number f; /* a font number for loops */
3811 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3812 mp_list_used_resources(mp, prologues, procset);
3813 mp_list_supplied_resources(mp, prologues, procset);
3814 mp_list_needed_resources(mp, prologues);
3815 mp_ps_print_nl(mp, "%%EndComments");
3816 mp_ps_print_nl(mp, "%%BeginProlog");
3818 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
3820 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3821 mp_ps_print_nl(mp, "/bd{bind def}bind def"
3822 "/fshow {exch findfont exch scalefont setfont show}bd");
3823 if ( procset>0 ) @<Print the procset@>;
3824 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
3825 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3826 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3827 "/fmd{/FontMatrix exch def}bd");
3828 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3829 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3830 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3831 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3832 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3833 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3834 mp_ps_print_nl(mp, "%%EndResource");
3835 @<Include encodings and fonts for edge structure~|h|@>;
3836 mp_ps_print_nl(mp, "%%EndProlog");
3837 mp_ps_print_nl(mp, "%%BeginSetup");
3839 for (f=null_font+1;f<=mp->last_fnum;f++) {
3840 if ( mp_has_font_size(mp,f) ) {
3841 if ( mp_has_fm_entry(mp,f,NULL) ) {
3842 mp_write_font_definition(mp,f, prologues);
3843 mp_ps_name_out(mp, mp->font_name[f],true);
3844 mp_ps_print_defined_name(mp,f, prologues);
3845 mp_ps_print(mp, " def");
3848 snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3850 mp_ps_name_out(mp, mp->font_name[f],true);
3851 mp_ps_name_out(mp, mp->font_name[f],true);
3852 mp_ps_print(mp, " def");
3857 mp_ps_print_nl(mp, "%%EndSetup");
3858 mp_ps_print_nl(mp, "%%Page: 1 1");
3860 mp_xfree(cur_fsize);
3864 static font_number mp_print_font_comments (MP mp , struct mp_edge_object *h, int prologues);
3869 static font_number mp_print_font_comments (MP mp , struct mp_edge_object *h, int prologues) {
3870 quarterword next_size; /* the size index for fonts being listed */
3871 pointer *cur_fsize; /* current positions in |font_sizes| */
3872 int ff; /* a loop counter */
3873 boolean done_fonts; /* have we finished listing the fonts in the header? */
3874 font_number f; /* a font number for loops */
3875 scaled ds; /* design size and scale factor for a text node */
3876 font_number ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3877 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3878 if ( prologues>0 ) {
3879 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3880 |font_sizes| and eliminate duplicates@>;
3883 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3884 do { done_fonts=true;
3885 for (f=null_font+1;f<=mp->last_fnum;f++) {
3886 if ( cur_fsize[f]!=null ) {
3887 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3889 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
3891 if ( ! done_fonts ) {
3892 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3895 } while (! done_fonts);
3897 mp_xfree(cur_fsize);
3901 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3902 for (f=null_font+1;f<= mp->last_fnum;f++)
3903 cur_fsize[f]=mp->font_sizes[f]
3905 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3906 so we carefully remove duplicates. There is no harm in using a slow, brute-force
3909 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3912 for (f=null_font+1;f<= mp->last_fnum;f++) {
3913 if ( mp->font_sizes[f]!=null ) {
3914 if ( ldf==null_font )
3915 mp_ps_print_nl(mp, "%%DocumentFonts:");
3916 for (ff=ldf;ff>=null_font;ff--) {
3917 if ( mp->font_sizes[ff]!=null )
3918 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3921 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3922 mp_ps_print_nl(mp, "%%+");
3923 mp_ps_print_char(mp, ' ');
3924 mp_ps_print(mp, mp->font_ps_name[f]);
3933 static void mp_hex_digit_out (MP mp,small_number d) {
3934 if ( d<10 ) mp_ps_print_char(mp, d+'0');
3935 else mp_ps_print_char(mp, d+'a'-10);
3938 @ We output the marks as a hexadecimal bit string starting at |c| or
3939 |font_bc[f]|, whichever is greater. If the output has to be truncated
3940 to avoid exceeding |emergency_line_length| the return value says where to
3941 start scanning next time.
3944 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3947 @d emergency_line_length 255
3948 /* \ps\ output lines can be this long in unusual circumstances */
3951 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3952 eight_bits bc,ec; /* only encode characters between these bounds */
3953 integer lim; /* the maximum number of marks to encode before truncating */
3954 int p; /* |font_info| index for the current character */
3955 int d,b; /* used to construct a hexadecimal digit */
3956 lim=4*(emergency_line_length-mp->ps->ps_offset-4);
3960 @<Restrict the range |bc..ec| so that it contains no unused characters
3961 at either end and has length at most |lim|@>;
3962 @<Print the initial label indicating that the bitmap starts at |bc|@>;
3963 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3964 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3970 @ We could save time by setting the return value before the loop that
3971 decrements |ec|, but there is no point in being so tricky.
3973 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3974 p=mp->char_base[f]+bc;
3975 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3978 if ( ec>=bc+lim ) ec=bc+lim-1;
3979 p=mp->char_base[f]+ec;
3980 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3984 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3985 mp_ps_print_char(mp, ' ');
3986 mp_hex_digit_out(mp, bc / 16);
3987 mp_hex_digit_out(mp, bc % 16);
3988 mp_ps_print_char(mp, ':')
3992 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3994 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3996 mp_hex_digit_out(mp, d);
3999 if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
4002 mp_hex_digit_out(mp, d)
4005 @ Here is a simple function that determines whether there are any marked
4006 characters in font~|f| with character code at least~|c|.
4009 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
4012 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
4013 int p; /* |font_info| index for the current character */
4014 for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
4015 if ( mp->font_info[p].qqqq.b3==mp_used )
4022 @ If the file name is so long that it can't be printed without exceeding
4023 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
4024 line. We might have to repeat line in order to get the character usage
4025 information to fit within |emergency_line_length|.
4027 TODO: these two defines are also defined in mp.w!
4029 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
4030 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
4032 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
4034 while ( mp_check_ps_marks(mp, f,t) ) {
4035 mp_ps_print_nl(mp, "%*Font: ");
4036 if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
4038 mp_ps_print(mp, mp->font_name[f]);
4039 mp_ps_print_char(mp, ' ');
4040 ds=(mp->font_dsize[f] + 8) / 16;
4041 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
4042 if ( mp->ps->ps_offset+12>emergency_line_length ) break;
4043 mp_ps_print_char(mp, ' ');
4044 mp_ps_print_scaled(mp, ds);
4045 if ( mp->ps->ps_offset+5>emergency_line_length ) break;
4046 t=mp_ps_marks_out(mp, f,t);
4048 cur_fsize[f]=link(cur_fsize[f]);
4051 @ @<Print the procset@>=
4053 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
4054 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
4055 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
4056 "/p{closepath}bd/n{newpath}bd");
4057 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
4058 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
4059 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
4060 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
4061 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4065 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4066 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
4067 might not have reasonable |font_ps_name| entries, but we just charge ahead
4068 anyway. The user should not make \&{prologues} positive if this will cause
4070 @:prologues_}{\&{prologues} primitive@>
4073 void mp_print_prologue (MP mp, struct mp_edge_object *h, int prologues, int procset);
4076 void mp_print_prologue (MP mp, struct mp_edge_object *h, int prologues, int procset) {
4079 ldf = mp_print_font_comments (mp, h, prologues);
4081 if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
4082 mp_read_psname_table(mp);
4083 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4084 if ( (prologues>0)||(procset>0) ) {
4085 if ( ldf!=null_font ) {
4086 if ( prologues>0 ) {
4087 for (f=null_font+1;f<=mp->last_fnum;f++) {
4088 if ( mp_has_font_size(mp,f) ) {
4089 mp_ps_name_out(mp, mp->font_name[f],true);
4090 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4091 mp_ps_print(mp, " def");
4096 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4102 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4103 if ( (prologues>0)&&(ldf!=null_font) )
4105 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4107 mp_ps_print_nl(mp, "/bd{bind def}bind def");
4108 @<Print the procset@>;
4109 mp_ps_print_nl(mp, "%%EndResource");
4113 mp_ps_print(mp, "%%EndProlog");
4114 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4117 @ \MP\ used to have one single routine to print to both `write' files
4118 and the PostScript output. Web2c redefines ``Character |k| cannot be
4119 printed'', and that resulted in some bugs where 8-bit characters were
4120 written to the PostScript file (reported by Wlodek Bzyl).
4122 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4123 a plain space, since that makes it easier to parse the result file
4126 @<Character |k| is not allowed in PostScript output@>=
4129 @ We often need to print a pair of coordinates.
4132 void mp_ps_pair_out (MP mp,scaled x, scaled y) {
4134 mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
4135 mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
4139 void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4142 void mp_ps_print_cmd (MP mp, char *l, char *s) {
4143 if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4144 else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4148 void mp_ps_print_cmd (MP mp, char *l, char *s) ;
4151 void mp_ps_string_out (MP mp, char *s) {
4152 ASCII_code k; /* bits to be converted to octal */
4153 mp_ps_print(mp, "(");
4155 if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4156 mp_ps_print_char(mp, '\\');
4159 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4160 mp_ps_print_char(mp, '\\');
4161 mp_ps_print_char(mp, '0'+(k / 64));
4162 mp_ps_print_char(mp, '0'+((k / 8) % 8));
4163 mp_ps_print_char(mp, '0'+(k % 8));
4165 if ( (k=='(')||(k==')')||(k=='\\') )
4166 mp_ps_print_char(mp, '\\');
4167 mp_ps_print_char(mp, k);
4170 mp_ps_print_char(mp, ')');
4174 void mp_ps_string_out (MP mp, char *s) ;
4176 @ This is a define because the function does not use its |mp| argument.
4178 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4181 static boolean mp_do_is_ps_name (char *s) {
4182 ASCII_code k; /* the character being checked */
4184 if ( (k<=' ')||(k>'~') ) return false;
4185 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4186 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4192 void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4195 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4196 ps_room(strlen(s)+2);
4197 mp_ps_print_char(mp, ' ');
4198 if ( mp_is_ps_name(mp, s) ) {
4199 if ( lit ) mp_ps_print_char(mp, '/');
4202 mp_ps_string_out(mp, s);
4203 if ( ! lit ) mp_ps_print(mp, "cvx ");
4204 mp_ps_print(mp, "cvn");
4209 @ These special comments described in the {\sl PostScript Language Reference
4210 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4211 We can't normally output ``conforming'' \ps\ because
4212 the structuring conventions don't allow us to say ``Please make sure the
4213 following characters are downloaded and define the \.{fshow} macro to access
4216 The exact bounding box is written out if |mp_prologues<0|, although this
4217 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4218 accurately. (Overfull boxes are avoided if an illustration is made to
4219 match a given \.{\char`\\hsize}.)
4222 void mp_print_initial_comment(MP mp,struct mp_edge_object *hh, int prologues);
4225 void mp_print_initial_comment(MP mp,struct mp_edge_object *hh, int prologues) {
4227 mp_ps_print(mp, "%!PS");
4229 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
4230 mp_ps_print_nl(mp, "%%BoundingBox: ");
4231 if ( hh->_minx>hh->_maxx) {
4232 mp_ps_print(mp, "0 0 0 0");
4233 } else if ( prologues<0 ) {
4234 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4235 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4237 mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->_minx),mp_floor_scaled(mp, hh->_miny));
4238 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->_maxx),-mp_floor_scaled(mp, -hh->_maxy));
4240 mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
4241 if ( hh->_minx>hh->_maxx ) {
4242 mp_ps_print(mp, "0 0 0 0");
4244 mp_ps_pair_out(mp, hh->_minx,hh->_miny);
4245 mp_ps_pair_out(mp, hh->_maxx,hh->_maxy);
4247 mp_ps_print_nl(mp, "%%Creator: MetaPost ");
4248 mp_ps_print(mp, mp_metapost_version(mp));
4249 mp_ps_print_nl(mp, "%%CreationDate: ");
4250 mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
4251 mp_ps_print_char(mp, '.');
4252 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month]));
4253 mp_ps_print_char(mp, '.');
4254 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
4255 mp_ps_print_char(mp, ':');
4256 t=mp_round_unscaled(mp, mp->internal[mp_time]);
4257 mp_ps_print_dd(mp, t / 60);
4258 mp_ps_print_dd(mp, t % 60);
4259 mp_ps_print_nl(mp, "%%Pages: 1");
4262 @ The most important output procedure is the one that gives the \ps\ version of
4266 #define gr_left_type(A) (A)->left_type_field
4267 #define gr_right_type(A) (A)->right_type_field
4268 #define gr_x_coord(A) (A)->x_coord_field
4269 #define gr_y_coord(A) (A)->y_coord_field
4270 #define gr_left_x(A) (A)->left_x_field
4271 #define gr_left_y(A) (A)->left_y_field
4272 #define gr_right_x(A) (A)->right_x_field
4273 #define gr_right_y(A) (A)->right_y_field
4274 #define gr_next_knot(A) (A)->next_field
4275 #define gr_originator(A) (A)->originator_field
4276 typedef struct mp_knot {
4277 unsigned short left_type_field;
4278 unsigned short right_type_field;
4279 scaled x_coord_field;
4280 scaled y_coord_field;
4281 scaled left_x_field;
4282 scaled left_y_field;
4283 scaled right_x_field;
4284 scaled right_y_field;
4285 struct mp_knot * next_field;
4286 quarterword originator_field;
4290 struct mp_knot * mp_gr_insert_knot (MP mp, struct mp_knot *q, scaled x, scaled y) {
4291 /* returns the inserted knot */
4292 struct mp_knot *r; /* the new knot */
4293 r= mp_xmalloc(mp, 1, sizeof (struct mp_knot));
4294 gr_next_knot(r)=gr_next_knot(q); gr_next_knot(q)=r;
4295 gr_right_x(r)=gr_right_x(q);
4296 gr_right_y(r)=gr_right_y(q);
4299 gr_right_x(q)=gr_x_coord(q);
4300 gr_right_y(q)=gr_y_coord(q);
4301 gr_left_x(r)=gr_x_coord(r);
4302 gr_left_y(r)=gr_y_coord(r);
4303 gr_left_type(r)=mp_explicit;
4304 gr_right_type(r)=mp_explicit;
4305 gr_originator(r)=mp_program_code;
4310 @ If we want to duplicate a knot node, we can say |copy_knot|:
4313 struct mp_knot *mp_gr_copy_knot (MP mp, struct mp_knot *p) {
4314 struct mp_knot *q; /* the copy */
4315 q = mp_xmalloc(mp, 1, sizeof (struct mp_knot));
4316 memcpy(q,p,sizeof (struct mp_knot));
4317 gr_next_knot(q)=NULL;
4321 @ The |copy_path| routine makes a clone of a given path.
4324 struct mp_knot *mp_gr_copy_path (MP mp, struct mp_knot *p) {
4325 struct mp_knot *q, *pp, *qq; /* for list manipulation */
4328 q=mp_gr_copy_knot(mp, p);
4332 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4333 qq=gr_next_knot(qq);
4334 pp=gr_next_knot(pp);
4340 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4341 returns a pointer to the first node of the copy, if the path is a cycle,
4342 but to the final node of a non-cyclic copy. The global
4343 variable |path_tail| will point to the final node of the original path;
4344 this trick makes it easier to implement `\&{doublepath}'.
4346 All node types are assumed to be |endpoint| or |explicit| only.
4348 This function is currenly unused.
4351 struct mp_knot * mp_gr_htap_ypoc (MP mp, struct mp_knot *p) {
4352 struct mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4353 q=mp_xmalloc(mp, 1, sizeof (struct mp_knot)); /* this will correspond to |p| */
4356 gr_right_type(qq)=gr_left_type(pp);
4357 gr_left_type(qq)=gr_right_type(pp);
4358 gr_x_coord(qq)=gr_x_coord(pp);
4359 gr_y_coord(qq)=gr_y_coord(pp);
4360 gr_right_x(qq)=gr_left_x(pp);
4361 gr_right_y(qq)=gr_left_y(pp);
4362 gr_left_x(qq)=gr_right_x(pp);
4363 gr_left_y(qq)=gr_right_y(pp);
4364 gr_originator(qq)=gr_originator(pp);
4365 if ( gr_next_knot(pp)==p ) {
4367 /* mp->path_tail=pp; */ /* ? */
4370 rr=mp_xmalloc(mp, 1, sizeof (struct mp_knot));
4371 gr_next_knot(rr)=qq;
4373 pp=gr_next_knot(pp);
4377 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4378 calling the following subroutine.
4381 void mp_do_gr_toss_knot_list (struct mp_knot *p) ;
4384 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4387 void mp_do_gr_toss_knot_list (struct mp_knot * p) {
4388 struct mp_knot *q; /* the node being freed */
4389 struct mp_knot *r; /* the next node */
4402 void mp_gr_ps_path_out (MP mp, struct mp_knot *h) {
4403 struct mp_knot *p, *q; /* for scanning the path */
4404 scaled d; /* a temporary value */
4405 boolean curved; /* |true| unless the cubic is almost straight */
4407 mp_ps_print_cmd(mp, "newpath ","n ");
4408 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4409 mp_ps_print_cmd(mp, "moveto","m");
4412 if ( gr_right_type(p)==mp_endpoint ) {
4413 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4417 @<Start a new line and print the \ps\ commands for the curve from
4421 mp_ps_print_cmd(mp, " closepath"," p");
4424 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4426 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4429 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4430 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4431 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4432 mp_ps_print_cmd(mp, "curveto","c");
4434 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4435 mp_ps_print_cmd(mp, "lineto","l");
4438 @ Two types of straight lines come up often in \MP\ paths:
4439 cubics with zero initial and final velocity as created by |make_path| or
4440 |make_envelope|, and cubics with control points uniformly spaced on a line
4441 as created by |make_choices|.
4443 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4445 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4446 if ( gr_right_x(p)==gr_x_coord(p) )
4447 if ( gr_right_y(p)==gr_y_coord(p) )
4448 if ( gr_left_x(q)==gr_x_coord(q) )
4449 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4450 d=gr_left_x(q)-gr_right_x(p);
4451 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4452 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4453 { d=gr_left_y(q)-gr_right_y(p);
4454 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4455 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4458 @ The colored objects use a union to express the color parts:
4463 scaled _red_val, _green_val, _blue_val;
4466 scaled _cyan_val, _magenta_val, _yellow_val, _black_val;
4475 @d gr_start_x(A) (A)->start_x_field
4476 @d gr_stop_x(A) (A)->stop_x_field
4477 @d gr_dash_link(A) (A)->next_field
4479 @d gr_dash_list(A) (A)->list_field
4480 @d gr_dash_y(A) (A)->y_field
4483 typedef struct mp_dash_item {
4484 scaled start_x_field;
4485 scaled stop_x_field;
4486 struct mp_dash_item *next_field;
4488 typedef struct mp_dash_list {
4489 struct mp_dash_item *list_field;
4495 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
4498 void mp_do_gr_toss_dashes(struct mp_dash_list *dl);
4501 void mp_do_gr_toss_dashes(struct mp_dash_list *dl) {
4502 struct mp_dash_item *di, *dn;
4503 di = gr_dash_list(dl);
4505 dn = gr_dash_link(di);
4514 struct mp_dash_list *mp_gr_copy_dashes(MP mp, struct mp_dash_list *dl) {
4515 struct mp_dash_list *q = NULL;
4524 @ Now for outputting the actual graphic objects. First, set up some
4525 structures and access macros.
4527 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4530 #define gr_type(A) (A)->_type_field
4531 #define gr_link(A) (A)->_link_field
4532 #define gr_name_type(A) (A)->name_type_field
4533 #define gr_color_model(A) (A)->color_model_field
4534 #define gr_red_val(A) (A)->color_field.rgb._red_val
4535 #define gr_green_val(A) (A)->color_field.rgb._green_val
4536 #define gr_blue_val(A) (A)->color_field.rgb._blue_val
4537 #define gr_cyan_val(A) (A)->color_field.cmyk._cyan_val
4538 #define gr_magenta_val(A) (A)->color_field.cmyk._magenta_val
4539 #define gr_yellow_val(A) (A)->color_field.cmyk._yellow_val
4540 #define gr_black_val(A) (A)->color_field.cmyk._black_val
4541 #define gr_grey_val(A) (A)->color_field.grey._grey_val
4542 #define gr_path_p(A) (A)->path_p_field
4543 #define gr_htap_p(A) (A)->htap_p_field
4544 #define gr_pen_p(A) (A)->pen_p_field
4545 #define gr_ljoin_val(A) (A)->ljoin_field
4546 #define gr_lcap_val(A) (A)->lcap_field
4547 #define gr_dash_scale(A) (A)->dash_scale_field
4548 #define gr_miterlim_val(A) (A)->miterlim_field
4549 #define gr_pre_script(A) (A)->pre_script_field
4550 #define gr_post_script(A) (A)->post_script_field
4551 #define gr_dash_p(A) (A)->dash_p_field
4552 #define gr_text_p(A) (A)->text_p_field
4553 #define gr_font_n(A) (A)->font_n_field
4554 #define gr_font_name(A) (A)->font_name_field
4555 #define gr_font_dsize(A) (A)->font_dsize_field
4556 #define gr_width_val(A) (A)->width_field
4557 #define gr_height_val(A) (A)->height_field
4558 #define gr_depth_val(A) (A)->depth_field
4559 #define gr_tx_val(A) (A)->tx_field
4560 #define gr_ty_val(A) (A)->ty_field
4561 #define gr_txx_val(A) (A)->txx_field
4562 #define gr_txy_val(A) (A)->txy_field
4563 #define gr_tyx_val(A) (A)->tyx_field
4564 #define gr_tyy_val(A) (A)->tyy_field
4565 typedef struct mp_graphic_object {
4566 halfword _type_field;
4567 quarterword name_type_field;
4568 struct mp_graphic_object * _link_field;
4569 struct mp_knot * path_p_field;
4570 struct mp_knot * htap_p_field;
4571 struct mp_knot * pen_p_field;
4572 quarterword color_model_field;
4573 mp_color color_field;
4574 quarterword ljoin_field ;
4575 quarterword lcap_field ;
4576 scaled miterlim_field ;
4577 scaled dash_scale_field ;
4578 char *pre_script_field;
4579 char *post_script_field;
4580 struct mp_dash_list *dash_p_field;
4582 font_number font_n_field ;
4583 char *font_name_field ;
4584 scaled font_dsize_field ;
4585 scaled width_field ;
4586 scaled height_field ;
4587 scaled depth_field ;
4594 } mp_graphic_object;
4595 typedef struct mp_edge_object {
4596 struct mp_graphic_object * body;
4597 struct mp_edge_object * _next;
4600 scaled _minx, _miny, _maxx, _maxy;
4603 @ @<Exported function headers@>=
4604 struct mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4607 struct mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4608 mp_graphic_object *p;
4609 p = mp_xmalloc(mp,1,sizeof(struct mp_graphic_object));
4610 memset(p,0,sizeof(struct mp_graphic_object));
4615 @ We need to keep track of several parameters from the \ps\ graphics state.
4617 This allows us to be sure that \ps\ has the correct values when they are
4618 needed without wasting time and space setting them unnecessarily.
4620 @d gs_red mp->ps->gs_state->red_field
4621 @d gs_green mp->ps->gs_state->green_field
4622 @d gs_blue mp->ps->gs_state->blue_field
4623 @d gs_black mp->ps->gs_state->black_field
4624 @d gs_colormodel mp->ps->gs_state->colormodel_field
4625 @d gs_ljoin mp->ps->gs_state->ljoin_field
4626 @d gs_lcap mp->ps->gs_state->lcap_field
4627 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4628 @d gs_miterlim mp->ps->gs_state->miterlim_field
4629 @d gs_dash_p mp->ps->gs_state->dash_p_field
4630 @d gs_previous mp->ps->gs_state->previous_field
4631 @d gs_dash_sc mp->ps->gs_state->dash_sc_field
4632 @d gs_width mp->ps->gs_state->width_field
4635 typedef struct _gs_state {
4637 scaled green_field ;
4639 scaled black_field ;
4640 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4641 quarterword colormodel_field ;
4642 /* the current colormodel */
4643 quarterword ljoin_field ;
4644 quarterword lcap_field ;
4645 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4646 quarterword adj_wx_field ;
4647 /* what resolution-dependent adjustment applies to the width */
4648 scaled miterlim_field ;
4649 /* the value from the last \&{setmiterlimit} command */
4650 struct mp_dash_list * dash_p_field ;
4651 /* edge structure for last \&{setdash} command */
4652 struct _gs_state * previous_field ;
4653 /* backlink to the previous |_gs_state| structure */
4654 scaled dash_sc_field ;
4655 /* scale factor used with |gs_dash_p| */
4656 scaled width_field ;
4657 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4662 struct _gs_state * gs_state;
4665 mp->ps->gs_state=NULL;
4667 @ @<Dealloc variables@>=
4668 mp_xfree(mp->ps->gs_state);
4670 @ To avoid making undue assumptions about the initial graphics state, these
4671 parameters are given special values that are guaranteed not to match anything
4672 in the edge structure being shipped out. On the other hand, the initial color
4673 should be black so that the translation of an all-black picture will have no
4674 \&{setcolor} commands. (These would be undesirable in a font application.)
4675 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4676 to recover from a situation where we have lost track of the graphics state.
4679 void mp_gs_unknown_graphics_state (MP mp,scaled c) ;
4682 @d mp_void (null+1) /* a null pointer different from |null| */
4684 @c void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4685 struct _gs_state *p; /* to shift graphic states around */
4686 if ( (c==0)||(c==-1) ) {
4687 if ( mp->ps->gs_state==NULL ) {
4688 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4691 while ( gs_previous!=NULL ) {
4693 mp_xfree(mp->ps->gs_state);
4697 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4698 gs_colormodel=mp_uninitialized_model;
4705 } else if ( c==1 ) {
4706 p= mp->ps->gs_state;
4707 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4708 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4710 } else if ( c==2 ) {
4712 mp_xfree(mp->ps->gs_state);
4718 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4719 that \ps's idea of the graphics state agrees with what is stored in the object.
4722 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) ;
4725 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) {
4726 /* get ready to output graphical object |p| */
4727 struct mp_knot *pp; /* for list manipulation */
4728 struct mp_dash_list *hh;
4729 scaled wx,wy,ww; /* dimensions of pen bounding box */
4730 boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4731 integer tx,ty; /* temporaries for computing |adj_wx| */
4732 scaled scf; /* a scale factor for the dash pattern */
4733 if ( gr_has_color(p) )
4734 @<Make sure \ps\ will use the right color for object~|p|@>;
4735 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) )
4736 if ( gr_pen_p(p)!=NULL )
4737 if ( pen_is_elliptical(gr_pen_p(p)) ) {
4738 @<Generate \ps\ code that sets the stroke width to the
4739 appropriate rounded value@>;
4740 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4741 @<Decide whether the line cap parameter matters and set it if necessary@>;
4742 @<Set the other numeric parameters as needed for object~|p|@>;
4744 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
4747 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4748 if ( gr_type(p)==mp_stroked_code )
4749 if ( (gr_left_type(gr_path_p(p))==mp_endpoint)||(gr_dash_p(p)!=NULL) )
4750 if ( gs_lcap!=gr_lcap_val(p) ) {
4752 mp_ps_print_char(mp, ' ');
4753 mp_ps_print_char(mp, '0'+gr_lcap_val(p));
4754 mp_ps_print_cmd(mp, " setlinecap"," lc");
4755 gs_lcap=gr_lcap_val(p);
4758 @ @<Set the other numeric parameters as needed for object~|p|@>=
4759 if ( gs_ljoin!=gr_ljoin_val(p) ) {
4761 mp_ps_print_char(mp, ' ');
4762 mp_ps_print_char(mp, '0'+gr_ljoin_val(p));
4763 mp_ps_print_cmd(mp, " setlinejoin"," lj");
4764 gs_ljoin=gr_ljoin_val(p);
4766 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4768 mp_ps_print_char(mp, ' ');
4769 mp_ps_print_scaled(mp, gr_miterlim_val(p));
4770 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4771 gs_miterlim=gr_miterlim_val(p);
4774 @ @<Make sure \ps\ will use the right color for object~|p|@>=
4776 if ( (gr_color_model(p)==mp_rgb_model)||
4777 ((gr_color_model(p)==mp_uninitialized_model)&&
4778 ((mp->internal[mp_default_color_model]>>16)==mp_rgb_model)) ) {
4779 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=gr_red_val(p))||
4780 (gs_green!=gr_green_val(p))||(gs_blue!=gr_blue_val(p)) ) {
4781 gs_red=gr_red_val(p);
4782 gs_green=gr_green_val(p);
4783 gs_blue=gr_blue_val(p);
4785 gs_colormodel=mp_rgb_model;
4787 mp_ps_print_char(mp, ' ');
4788 mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
4789 mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
4790 mp_ps_print_scaled(mp, gs_blue);
4791 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4794 } else if ( (gr_color_model(p)==mp_cmyk_model)||
4795 ((gr_color_model(p)==mp_uninitialized_model)&&
4796 ((mp->internal[mp_default_color_model]>>16)==mp_cmyk_model)) ) {
4797 if ( (gs_red!=gr_cyan_val(p))||(gs_green!=gr_magenta_val(p))||
4798 (gs_blue!=gr_yellow_val(p))||(gs_black!=gr_black_val(p))||
4799 (gs_colormodel!=mp_cmyk_model) ) {
4800 if ( gr_color_model(p)==mp_uninitialized_model ) {
4806 gs_red=gr_cyan_val(p);
4807 gs_green=gr_magenta_val(p);
4808 gs_blue=gr_yellow_val(p);
4809 gs_black=gr_black_val(p);
4811 gs_colormodel=mp_cmyk_model;
4813 mp_ps_print_char(mp, ' ');
4814 mp_ps_print_scaled(mp, gs_red);
4815 mp_ps_print_char(mp, ' ');
4816 mp_ps_print_scaled(mp, gs_green);
4817 mp_ps_print_char(mp, ' ');
4818 mp_ps_print_scaled(mp, gs_blue);
4819 mp_ps_print_char(mp, ' ');
4820 mp_ps_print_scaled(mp, gs_black);
4821 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4824 } else if ( (gr_color_model(p)==mp_grey_model)||
4825 ((gr_color_model(p)==mp_uninitialized_model)&&
4826 ((mp->internal[mp_default_color_model]>>16)==mp_grey_model)) ) {
4827 if ( (gs_red!=gr_grey_val(p))||(gs_colormodel!=mp_grey_model) ) {
4828 gs_red = gr_grey_val(p);
4832 gs_colormodel=mp_grey_model;
4834 mp_ps_print_char(mp, ' ');
4835 mp_ps_print_scaled(mp, gs_red);
4836 mp_ps_print_cmd(mp, " setgray"," G");
4840 if ( gr_color_model(p)==mp_no_model )
4841 gs_colormodel=mp_no_model;
4844 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4845 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4846 @:setwidth}{\&{setwidth}command@>
4847 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4848 generate \ps\ code that computes the rounded value. For non-circular pens, the
4849 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4850 have the computed width.
4852 Rounding the width to whole pixels is not likely to improve the appearance of
4853 diagonal or curved strokes, but we do it anyway for consistency. The
4854 \&{truncate} command generated here tends to make all the strokes a little
4855 @:truncate}{\&{truncate} command@>
4856 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4857 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4858 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4861 @<Generate \ps\ code that sets the stroke width...@>=
4862 @<Set |wx| and |wy| to the width and height of the bounding box for
4864 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4865 important and set |adj_wx| and |ww| accordingly@>;
4866 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4869 mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
4871 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4873 if ( mp->internal[mp_procset]>0 ) {
4875 mp_ps_print_char(mp, ' ');
4876 mp_ps_print_scaled(mp, ww);
4877 mp_ps_print(mp, " vlw");
4880 mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
4881 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4888 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4890 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4891 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4892 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4894 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4895 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4898 @ The path is considered ``essentially horizontal'' if its range of
4899 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4900 vertical'' paths are detected similarly. This code ensures that no component
4901 of the pen transformation is more that |aspect_bound*(ww+1)|.
4903 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4904 more than this factor */
4909 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4911 if ( mp_gr_coord_rangeOK(gr_path_p(p), do_y_loc, wy) ) tx=aspect_bound;
4912 else if ( mp_gr_coord_rangeOK(gr_path_p(p), do_x_loc, wx) ) ty=aspect_bound;
4913 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
4914 else { ww=wx; adj_wx=true; }
4916 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4917 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4918 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4919 bounding-box computation.
4922 boolean mp_gr_coord_rangeOK (struct mp_knot *h,
4923 small_number zoff, scaled dz);
4926 boolean mp_gr_coord_rangeOK (struct mp_knot *h,
4927 small_number zoff, scaled dz) {
4928 struct mp_knot *p; /* for scanning the path form |h| */
4929 scaled zlo,zhi; /* coordinate range so far */
4930 scaled z; /* coordinate currently being tested */
4931 if (zoff==do_x_loc) {
4935 while ( gr_right_type(p)!=mp_endpoint ) {
4937 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4938 p=gr_next_knot(p); z=gr_left_x(p);
4939 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4941 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4948 while ( gr_right_type(p)!=mp_endpoint ) {
4950 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4951 p=gr_next_knot(p); z=gr_left_y(p);
4952 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4954 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4961 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4963 else if ( z>zhi ) zhi=z;
4964 if ( zhi-zlo>dz ) return false
4966 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4967 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4968 @:stroke}{\&{stroke} command@>
4969 @:fill}{\&{fill} command@>
4970 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4973 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4974 if ( gr_type(p)==mp_fill_code ) {
4978 scf=mp_gr_get_pen_scale(mp, gr_pen_p(p));
4980 if ( gs_width==0 ) scf=gr_dash_scale(p); else hh=NULL;
4982 scf=mp_make_scaled(mp, gs_width,scf);
4983 scf=mp_take_scaled(mp, scf,gr_dash_scale(p));
4987 if ( gs_dash_p!=NULL ) {
4988 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4991 } else if ( (gs_dash_sc!=scf) || ! mp_gr_same_dashes(gs_dash_p,hh) ) {
4992 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
4996 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) ;
5000 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) {
5001 return mp_sqrt_det(mp,
5002 gr_left_x(p)-gr_x_coord(p), gr_right_x(p)-gr_x_coord(p),
5003 gr_left_y(p)-gr_y_coord(p), gr_right_y(p)-gr_y_coord(p));
5007 @ Translating a dash list into \ps\ is very similar to printing it symbolically
5008 in |print_edges|. A dash pattern with |dash_y(hh)=0| has length zero and is
5009 ignored. The same fate applies in the bizarre case of a dash pattern that
5010 cannot be printed without overflow.
5012 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5015 if ( (gr_dash_p(p)==NULL) ||
5016 (gr_dash_y(hh)==0) ||
5017 ((abs(gr_dash_y(hh)) / unity) >= (el_gordo / scf))) {
5018 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5020 struct mp_dash_item *dpp=gr_dash_list(hh);
5021 struct mp_dash_item *pp= dpp;
5023 mp_ps_print(mp, " [");
5024 while ( dpp!=NULL ) {
5026 dx = mp_take_scaled(mp, gr_stop_x(dpp)-gr_start_x(dpp),scf);
5028 if (gr_dash_link(dpp)!=NULL) {
5029 dy = mp_take_scaled(mp, gr_start_x(gr_dash_link(dpp))-gr_stop_x(dpp),scf);
5031 dy = mp_take_scaled(mp, (gr_start_x(pp)+gr_dash_y(hh))-gr_stop_x(dpp),scf);
5033 mp_ps_pair_out(mp, dx, dy);
5034 dpp=gr_dash_link(dpp);
5037 mp_ps_print(mp, "] ");
5038 mp_ps_print_scaled(mp, mp_take_scaled(mp, mp_gr_dash_offset(mp, hh),scf));
5039 mp_ps_print_cmd(mp, " setdash"," sd");
5044 boolean mp_gr_same_dashes (struct mp_dash_list *h, struct mp_dash_list *hh) ;
5047 boolean mp_gr_same_dashes (struct mp_dash_list * h, struct mp_dash_list *hh) {
5048 /* do |h| and |hh| represent the same dash pattern? */
5049 struct mp_dash_item * p, *pp; /* dash nodes being compared */
5050 if ( h==hh ) return true;
5051 else if ( (h==NULL)||(hh==NULL) ) return false;
5052 else if ( gr_dash_y(h)!=gr_dash_y(hh) ) return false;
5053 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5054 return false; /* can't happen */
5057 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5058 { p=gr_dash_list(h);
5059 pp=gr_dash_list(hh);
5060 while ( (p!=NULL)&&(pp!=NULL) ) {
5061 if ( (gr_start_x(p)!=gr_start_x(pp))||
5062 (gr_stop_x(p)!=gr_stop_x(pp)) ) {
5066 pp=gr_dash_link(pp);
5073 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) ;
5076 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) {
5077 scaled x; /* the answer */
5078 if ( h==NULL || (gr_dash_list(h)==NULL) || (gr_dash_y(h)<0) )
5079 mp_confusion(mp, "dash0");
5080 @:this can't happen dash0}{\quad dash0@>
5081 if ( gr_dash_y(h)==0 ) {
5084 x=-(gr_start_x(gr_dash_list(h)) % gr_dash_y(h));
5085 if ( x<0 ) x=x+gr_dash_y(h);
5090 @ When stroking a path with an elliptical pen, it is necessary to transform
5091 the coordinate system so that a unit circular pen will have the desired shape.
5092 To keep this transformation local, we enclose it in a
5093 $$\&{gsave}\ldots\&{grestore}$$
5094 block. Any translation component must be applied to the path being stroked
5095 while the rest of the transformation must apply only to the pen.
5096 If |fill_also=true|, the path is to be filled as well as stroked so we must
5097 insert commands to do this after giving the path.
5100 void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) ;
5103 @c void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) {
5104 /* generate an elliptical pen stroke from object |h| */
5105 scaled txx,txy,tyx,tyy; /* transformation parameters */
5106 struct mp_knot *p; /* the pen to stroke with */
5107 scaled d1,det; /* for tweaking transformation parameters */
5108 integer s; /* also for tweaking transformation paramters */
5109 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5111 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5113 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5114 mp_gr_ps_path_out(mp, gr_path_p(h));
5115 if ( mp->internal[mp_procset]==0 ) {
5116 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5117 @<Issue \ps\ commands to transform the coordinate system@>;
5118 mp_ps_print(mp, " stroke");
5119 if ( transformed ) mp_ps_print(mp, " grestore");
5121 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5122 if ( (txy!=0)||(tyx!=0) ) {
5123 mp_ps_print(mp, " [");
5124 mp_ps_pair_out(mp, txx,tyx);
5125 mp_ps_pair_out(mp, txy,tyy);
5126 mp_ps_print(mp, "0 0] t");
5127 } else if ((txx!=unity)||(tyy!=unity) ) {
5128 mp_ps_pair_out(mp,txx,tyy);
5129 mp_ps_print(mp, " s");
5131 mp_ps_print(mp, " S");
5132 if ( transformed ) mp_ps_print(mp, " Q");
5137 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5143 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5144 mp_ps_print_nl(mp, "");
5145 mp_ps_print_cmd(mp, "gsave ","q ");
5146 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5147 mp_ps_print(mp, "translate ");
5154 mp_ps_print_nl(mp, "");
5156 @<Adjust the transformation to account for |gs_width| and output the
5157 initial \&{gsave} if |transformed| should be |true|@>
5159 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5160 if ( gs_width!=unity ) {
5161 if ( gs_width==0 ) {
5162 txx=unity; tyy=unity;
5164 txx=mp_make_scaled(mp, txx,gs_width);
5165 txy=mp_make_scaled(mp, txy,gs_width);
5166 tyx=mp_make_scaled(mp, tyx,gs_width);
5167 tyy=mp_make_scaled(mp, tyy,gs_width);
5170 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5171 if ( (! transformed) ){
5172 mp_ps_print_cmd(mp, "gsave ","q ");
5177 @ @<Issue \ps\ commands to transform the coordinate system@>=
5178 if ( (txy!=0)||(tyx!=0) ){
5180 mp_ps_print_char(mp, '[');
5181 mp_ps_pair_out(mp, txx,tyx);
5182 mp_ps_pair_out(mp, txy,tyy);
5183 mp_ps_print(mp, "0 0] concat");
5184 } else if ( (txx!=unity)||(tyy!=unity) ){
5186 mp_ps_pair_out(mp, txx,tyy);
5187 mp_ps_print(mp, "scale");
5190 @ The \ps\ interpreter will probably abort if it encounters a singular
5191 transformation matrix. The determinant must be large enough to ensure that
5192 the printed representation will be nonsingular. Since the printed
5193 representation is always within $2^{-17}$ of the internal |scaled| value, the
5194 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5195 the magnitudes of |txx/65536|, |txy/65536|, etc.
5197 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5198 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5200 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5201 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5202 d1=4*aspect_bound+1;
5203 if ( abs(det)<d1 ) {
5204 if ( det>=0 ) { d1=d1-det; s=1; }
5205 else { d1=-d1-det; s=-1; };
5207 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5208 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5209 else txx=txx+(d1+s*abs(tyy)) / tyy;
5211 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5212 else txy=txy+(d1+s*abs(tyx)) / tyx;
5216 @ Here is a simple routine that just fills a cycle.
5219 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p);
5222 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p) { /* fill cyclic path~|p| */
5223 mp_gr_ps_path_out(mp, p);
5224 mp_ps_print_cmd(mp, " fill"," F");
5228 @ A text node may specify an arbitrary transformation but the usual case
5229 involves only shifting, scaling, and occasionally rotation. The purpose
5230 of |choose_scale| is to select a scale factor so that the remaining
5231 transformation is as ``nice'' as possible. The definition of ``nice''
5232 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5233 nice because they work out well for bitmap fonts. The code here selects
5234 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5235 non-shifting part of the transformation matrix. It is careful to avoid
5236 additions that might cause undetected overflow.
5239 scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) ;
5241 @ @c scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) {
5242 /* |p| should point to a text node */
5243 scaled a,b,c,d,ad,bc; /* temporary values */
5248 if ( (a<0) ) negate(a);
5249 if ( (b<0) ) negate(b);
5250 if ( (c<0) ) negate(c);
5251 if ( (d<0) ) negate(d);
5254 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5257 @ The potential overflow here is caused by the fact the returned value
5258 has to fit in a |name_type|, which is a quarterword.
5260 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
5263 quarterword mp_size_index (MP mp, font_number f, scaled s) ;
5266 quarterword mp_size_index (MP mp, font_number f, scaled s) {
5267 pointer p,q; /* the previous and current font size nodes */
5268 quarterword i; /* the size index for |q| */
5269 q=mp->font_sizes[f];
5272 if ( abs(s-sc_factor(q))<=fscale_tolerance )
5275 { p=q; q=link(q); incr(i); };
5276 if ( i==max_quarterword )
5277 mp_overflow(mp, "sizes per font",max_quarterword);
5278 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
5280 q=mp_get_node(mp, font_size_size);
5282 if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
5287 scaled mp_indexed_size (MP mp,font_number f, quarterword j);
5290 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
5291 pointer p; /* a font size node */
5292 quarterword i; /* the size index for |p| */
5293 p=mp->font_sizes[f];
5295 if ( p==null ) mp_confusion(mp, "size");
5298 if ( p==null ) mp_confusion(mp, "size");
5300 return sc_factor(p);
5304 void mp_clear_sizes (MP mp) ;
5306 @ @c void mp_clear_sizes (MP mp) {
5307 font_number f; /* the font whose size list is being cleared */
5308 pointer p; /* current font size nodes */
5309 for (f=null_font+1;f<=mp->last_fnum;f++) {
5310 while ( mp->font_sizes[f]!=null ) {
5311 p=mp->font_sizes[f];
5312 mp->font_sizes[f]=link(p);
5313 mp_free_node(mp, p,font_size_size);
5318 @ A text node may specify an arbitrary transformation but the usual case
5319 involves only shifting, scaling, and occasionally rotation. The purpose
5320 of |choose_scale| is to select a scale factor so that the remaining
5321 transformation is as ``nice'' as possible. The definition of ``nice''
5322 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5323 nice because they work out well for bitmap fonts. The code here selects
5324 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5325 non-shifting part of the transformation matrix. It is careful to avoid
5326 additions that might cause undetected overflow.
5328 @<Declare the \ps\ output procedures@>=
5329 scaled mp_choose_scale (MP mp, mp_graphic_object *p) ;
5331 @ @c scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
5332 /* |p| should point to a text node */
5333 scaled a,b,c,d,ad,bc; /* temporary values */
5338 if ( (a<0) ) negate(a);
5339 if ( (b<0) ) negate(b);
5340 if ( (c<0) ) negate(c);
5341 if ( (d<0) ) negate(d);
5344 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5347 @ There may be many sizes of one font and we need to keep track of the
5348 characters used for each size. This is done by keeping a linked list of
5349 sizes for each font with a counter in each text node giving the appropriate
5350 position in the size list for its font.
5352 @d font_size_size 2 /* size of a font size node */
5356 void mp_apply_mark_string_chars(MP mp, struct mp_edge_object *h, int next_size);
5359 void mp_apply_mark_string_chars(MP mp, struct mp_edge_object *h, int next_size) {
5360 struct mp_graphic_object * p;
5362 while ( p!= NULL ) {
5363 if ( gr_type(p)==mp_text_code ) {
5364 if ( gr_font_n(p)!=null_font ) {
5365 if ( gr_name_type(p)==next_size )
5366 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p));
5373 @ @<Unmark all marked characters@>=
5374 for (f=null_font+1;f<=mp->last_fnum;f++) {
5375 if ( mp->font_sizes[f]!=null ) {
5376 mp_unmark_font(mp, f);
5377 mp->font_sizes[f]=null;
5381 @ @<Scan all the text nodes and mark the used ...@>=
5384 if ( gr_type(p)==mp_text_code ) {
5386 if (f!=null_font ) {
5387 switch (prologues) {
5390 mp->font_sizes[f] = mp_void;
5391 mp_mark_string_chars(mp, f, gr_text_p(p));
5392 if (mp_has_fm_entry(mp,f,NULL) ) {
5393 if (mp->font_enc_name[f]==NULL )
5394 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
5395 mp->font_ps_name[f] = mp_fm_font_name(mp,f);
5399 mp->font_sizes[f]=mp_void;
5402 gr_name_type(p)=mp_size_index(mp, f,mp_choose_scale(mp, p));
5403 if ( gr_name_type(p)==0 )
5404 mp_mark_string_chars(mp, f, gr_text_p(p));
5413 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5415 @<Exported function headers@>=
5416 void mp_gr_ship_out (struct mp_edge_object *hh, int prologues, int procset) ;
5419 void mp_gr_ship_out (struct mp_edge_object *hh, int prologues, int procset) {
5420 struct mp_graphic_object *p;
5421 scaled ds,scf; /* design size and scale factor for a text node */
5422 font_number f; /* for loops over fonts while (un)marking characters */
5423 boolean transformed; /* is the coordinate system being transformed? */
5424 MP mp = hh->_parent;
5425 if (mp->history >= mp_fatal_error_stop ) return;
5427 prologues = (mp->internal[mp_prologues]>>16);
5429 procset = (mp->internal[mp_procset]>>16);
5430 mp_open_output_file(mp);
5431 mp_print_initial_comment(mp, hh, prologues);
5433 @<Unmark all marked characters@>;
5434 if ( prologues==2 || prologues==3 ) {
5435 mp_reload_encodings(mp);
5437 @<Scan all the text nodes and mark the used characters@>;
5438 if ( prologues==2 || prologues==3 ) {
5439 mp_print_improved_prologue(mp, hh, prologues, procset);
5441 mp_print_prologue(mp, hh, prologues, procset);
5443 mp_gs_unknown_graphics_state(mp, 0);
5446 if ( gr_has_color(p) ) {
5447 if ( (gr_pre_script(p))!=NULL ) {
5448 mp_ps_print_nl (mp, gr_pre_script(p));
5452 mp_gr_fix_graphics_state(mp, p);
5453 switch (gr_type(p)) {
5455 if ( gr_pen_p(p)==NULL ) {
5456 mp_gr_ps_fill_out(mp, gr_path_p(p));
5457 } else if ( pen_is_elliptical(gr_pen_p(p)) ) {
5458 mp_gr_stroke_ellipse(mp, p,true);
5460 mp_gr_ps_fill_out(mp, gr_path_p(p));
5461 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5463 if ( gr_post_script(p)!=NULL ) {
5464 mp_ps_print_nl (mp, gr_post_script(p));
5468 case mp_stroked_code:
5469 if ( pen_is_elliptical(gr_pen_p(p)) ) mp_gr_stroke_ellipse(mp, p,false);
5471 mp_gr_ps_fill_out(mp, gr_path_p(p));
5473 if ( gr_post_script(p)!=NULL ) {
5474 mp_ps_print_nl (mp, gr_post_script(p)); mp_ps_print_ln(mp);
5478 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5480 scf=mp_gr_choose_scale(mp, p);
5482 scf=mp_indexed_size(mp, gr_font_n(p), gr_name_type(p));
5483 @<Shift or transform as necessary before outputting text node~|p| at scale
5484 factor~|scf|; set |transformed:=true| if the original transformation must
5486 mp_ps_string_out(mp, gr_text_p(p));
5487 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5488 @<Print the size information and \ps\ commands for text node~|p|@>;
5491 if ( gr_post_script(p)!=NULL ) {
5492 mp_ps_print_nl (mp, gr_post_script(p)); mp_ps_print_ln(mp);
5495 case mp_start_clip_code:
5496 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5497 mp_gr_ps_path_out(mp, gr_path_p(p));
5498 mp_ps_print_cmd(mp, " clip"," W");
5500 if ( mp->internal[mp_restore_clip_color]>0 )
5501 mp_gs_unknown_graphics_state(mp, 1);
5503 case mp_stop_clip_code:
5504 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5506 if ( mp->internal[mp_restore_clip_color]>0 )
5507 mp_gs_unknown_graphics_state(mp, 2);
5509 mp_gs_unknown_graphics_state(mp, -1);
5511 case mp_start_bounds_code:
5512 case mp_stop_bounds_code:
5514 case mp_special_code:
5515 mp_ps_print_nl (mp, gr_pre_script(p));
5516 mp_ps_print_ln (mp);
5518 } /* all cases are enumerated */
5521 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
5522 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
5523 (mp->close_file)(mp,mp->ps_file);
5528 @ The envelope of a cyclic path~|q| could be computed by calling
5529 |make_envelope| once for |q| and once for its reversal. We don't do this
5530 because it would fail color regions that are covered by the pen regardless
5531 of where it is placed on~|q|.
5533 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5534 if ( gr_left_type(q)!=mp_endpoint ) {
5535 gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5536 gr_right_type(q)=mp_endpoint;
5541 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5543 mp_ps_print_char(mp, ' ');
5544 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5545 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5546 mp_ps_print(mp, " fshow");
5548 mp_ps_print_cmd(mp, " grestore"," Q")
5552 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5553 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5554 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5555 if ( transformed ) {
5556 mp_ps_print_cmd(mp, "gsave [", "q [");
5557 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5558 mp_make_scaled(mp, gr_tyx_val(p),scf));
5559 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5560 mp_make_scaled(mp, gr_tyy_val(p),scf));
5561 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5562 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5564 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5565 mp_ps_print_cmd(mp, "moveto","m");
5570 @ @<Exported function headers@>=
5571 void mp_gr_toss_objects ( struct mp_edge_object *hh) ;
5572 void mp_gr_toss_object (struct mp_graphic_object *p) ;
5575 void mp_gr_toss_object (struct mp_graphic_object *p) {
5576 switch (gr_type(p)) {
5578 mp_xfree(gr_pre_script(p));
5579 mp_xfree(gr_post_script(p));
5580 mp_gr_toss_knot_list(mp,gr_pen_p(p));
5581 mp_gr_toss_knot_list(mp,gr_path_p(p));
5582 mp_gr_toss_knot_list(mp,gr_htap_p(p));
5584 case mp_stroked_code:
5585 mp_xfree(gr_pre_script(p));
5586 mp_xfree(gr_post_script(p));
5587 mp_gr_toss_knot_list(mp,gr_pen_p(p));
5588 mp_gr_toss_knot_list(mp,gr_path_p(p));
5589 if (gr_dash_p(p)!=NULL)
5590 mp_gr_toss_dashes (mp,gr_dash_p(p));
5593 mp_xfree(gr_pre_script(p));
5594 mp_xfree(gr_post_script(p));
5595 mp_xfree(gr_text_p(p));
5596 mp_xfree(gr_font_name(p));
5598 case mp_start_clip_code:
5599 case mp_start_bounds_code:
5600 mp_gr_toss_knot_list(mp,gr_path_p(p));
5602 case mp_stop_clip_code:
5603 case mp_stop_bounds_code:
5605 case mp_special_code:
5606 mp_xfree(gr_pre_script(p));
5608 } /* all cases are enumerated */
5614 void mp_gr_toss_objects (struct mp_edge_object *hh) {
5615 struct mp_graphic_object *p, *q;
5619 mp_gr_toss_object(p);
5625 @ @<Exported function headers@>=
5626 struct mp_graphic_object *mp_gr_copy_object (MP mp, struct mp_graphic_object *p) ;
5629 struct mp_graphic_object *
5630 mp_gr_copy_object (MP mp, struct mp_graphic_object *p) {
5631 struct mp_graphic_object *q;
5632 q = mp_xmalloc(mp,1,sizeof(mp_graphic_object));
5633 memcpy(q,p,sizeof(mp_graphic_object));
5635 switch (gr_type(p)) {
5637 gr_pre_script(q) = mp_xstrdup(mp, gr_pre_script(p));
5638 gr_post_script(q) = mp_xstrdup(mp, gr_post_script(p));
5639 gr_path_p(q) = mp_gr_copy_path(mp,gr_path_p(p));
5640 gr_htap_p(q) = mp_gr_copy_path(mp,gr_htap_p(p));
5641 gr_pen_p(q) = mp_gr_copy_path(mp,gr_pen_p(p));
5643 case mp_stroked_code:
5644 gr_pre_script(q) = mp_xstrdup(mp, gr_pre_script(p));
5645 gr_post_script(q) = mp_xstrdup(mp, gr_post_script(p));
5646 gr_path_p(q) = mp_gr_copy_path(mp,gr_path_p(p));
5647 gr_pen_p(q) = mp_gr_copy_path(mp,gr_pen_p(p));
5648 gr_dash_p(q) = mp_gr_copy_dashes(mp,gr_dash_p(p));
5651 gr_pre_script(q) = mp_xstrdup(mp, gr_pre_script(p));
5652 gr_post_script(q) = mp_xstrdup(mp, gr_post_script(p));
5653 gr_text_p(q) = mp_xstrdup(mp, gr_text_p(p));
5654 gr_font_name(q) = mp_xstrdup(mp, gr_font_name(p));
5656 case mp_start_clip_code:
5657 case mp_start_bounds_code:
5658 gr_path_p(q) = mp_gr_copy_path(mp,gr_path_p(p));
5660 case mp_special_code:
5661 gr_pre_script(q) = mp_xstrdup(mp, gr_pre_script(p));
5663 case mp_stop_clip_code:
5664 case mp_stop_bounds_code:
5666 } /* all cases are enumerated */