3 % Copyright 2008 Taco Hoekwater.
5 % This program is free software: you can redistribute it and/or modify
6 % it under the terms of the GNU General Public License as published by
7 % the Free Software Foundation, either version 2 of the License, or
8 % (at your option) any later version.
10 % This program is distributed in the hope that it will be useful,
11 % but WITHOUT ANY WARRANTY; without even the implied warranty of
12 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 % GNU General Public License for more details.
15 % You should have received a copy of the GNU General Public License
16 % along with this program. If not, see <http://www.gnu.org/licenses/>.
18 % TeX is a trademark of the American Mathematical Society.
19 % METAFONT is a trademark of Addison-Wesley Publishing Company.
20 % PostScript is a trademark of Adobe Systems Incorporated.
22 % Here is TeX material that gets inserted after \input webmac
23 \def\hang{\hangindent 3em\noindent\ignorespaces}
24 \def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
27 \def\ph{\hbox{Pascal-H}}
28 \def\psqrt#1{\sqrt{\mathstrut#1}}
30 \def\pct!{{\char`\%}} % percent sign in ordinary text
31 \font\tenlogo=logo10 % font used for the METAFONT logo
33 \def\MF{{\tenlogo META}\-{\tenlogo FONT}}
34 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
35 \def\<#1>{$\langle#1\rangle$}
36 \def\section{\mathhexbox278}
37 \let\swap=\leftrightarrow
38 \def\round{\mathop{\rm round}\nolimits}
39 \mathchardef\vbv="026A % synonym for `\|'
40 \def\vb{\relax\ifmmode\vbv\else$\vbv$\fi}
41 \def\[#1]{} % from pascal web
42 \def\(#1){} % this is used to make section names sort themselves better
43 \def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
45 \let\?=\relax % we want to be able to \write a \?
47 \def\title{MetaPost \ps\ output}
48 \def\topofcontents{\hsize 5.5in
49 \vglue -30pt plus 1fil minus 1.5in
50 \def\?##1]{\hbox to 1in{\hfil##1.\ }}
52 \def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
61 @d unity 0200000 /* $2^{16}$, represents 1.00000 */
62 @d el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
63 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
64 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
65 @d negate(A) (A)=-(A) /* change the sign of a variable */
68 @d print_err(A) mp_print_err(mp,(A))
69 @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
79 #include "mplibps.h" /* external header */
80 #include "mpmp.h" /* internal header */
81 #include "mppsout.h" /* internal header */
84 @<Static variables in the outer block@>
86 @ There is a small bit of code from the backend that bleads through
87 to the frontend because I do not know how to set up the includes
88 properly. Those are the definitions of |struct libavl_allocator|
89 and |typedef struct psout_data_struct * psout_data|.
91 The |libavl_allocator| is a trick that makes sure that frontends
92 do not need |avl.h|, and the |psout_data| is needed for the backend
97 typedef struct psout_data_struct {
100 @<Exported function headers@>
103 static boolean mp_isdigit (int a) {
104 return (a>='0'&&a<='9');
106 static int mp_tolower (int a) {
107 if (a>='A' && a <='Z')
108 return a - 'A' + 'a';
111 static int mp_strcasecmp (const char *s1, const char *s2) {
117 *c = mp_tolower(*c); c++;
122 *c = mp_tolower(*c); c++;
125 free (ss1); free(ss2);
129 @ @<Exported function headers@>=
130 void mp_backend_initialize (MP mp) ;
131 void mp_backend_free (MP mp) ;
134 @c void mp_backend_initialize (MP mp) {
135 mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
136 @<Set initial values@>;
138 void mp_backend_free (MP mp) {
139 @<Dealloc variables@>;
147 @ Writing to ps files
151 /* the number of characters on the current \ps\ file line */
153 @ @<Set initial values@>=
154 mp->ps->ps_offset = 0;
158 @d wps(A) (mp->write_ascii_file)(mp,mp->ps_file,(A))
162 (mp->write_ascii_file)(mp,mp->ps_file,(char *)ss);
164 @d wps_cr (mp->write_ascii_file)(mp,mp->ps_file,"\n")
165 @d wps_ln(A) { wterm_cr; (mp->write_ascii_file)(mp,mp->ps_file,(A)); }
168 static void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
174 static void mp_ps_print_char (MP mp, int s) { /* prints a single character */
176 wps_cr; mp->ps->ps_offset=0;
178 wps_chr(s); incr(mp->ps->ps_offset);
183 static void mp_ps_do_print (MP mp, const char *ss, size_t len) { /* prints string |s| */
186 mp_ps_print_char(mp, ss[j]); incr(j);
190 @ Deciding where to break the ps output line.
192 @d ps_room(A) if ( (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) {
193 mp_ps_print_ln(mp); /* optional line break */
197 static void mp_ps_print (MP mp, const char *ss) {
199 mp_ps_do_print(mp, ss, strlen(ss));
202 @ The procedure |print_nl| is like |print|, but it makes sure that the
203 string appears at the beginning of a new line.
206 static void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */
207 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
211 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
214 static void mp_ps_print_the_digs (MP mp, int k) {
215 /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
217 mp_ps_print_char(mp, '0'+mp->dig[k]);
221 @ The following procedure, which prints out the decimal representation of a
222 given integer |n|, has been written carefully so that it works properly
223 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
224 to negative arguments, since such operations are not implemented consistently
225 by all \PASCAL\ compilers.
228 static void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */
229 integer m; /* used to negate |n| in possibly dangerous cases */
230 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
232 mp_ps_print_char(mp, '-');
233 if ( n>-100000000 ) {
236 m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
238 mp->dig[0]=(unsigned char)m;
240 mp->dig[0]=0; incr(n);
245 mp->dig[k]=(unsigned char)(n % 10); n=n / 10; incr(k);
247 mp_ps_print_the_digs(mp, k);
250 @ \MP\ also makes use of a trivial procedure to print two digits. The
251 following subroutine is usually called with a parameter in the range |0<=n<=99|.
254 static void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */
256 mp_ps_print_char(mp, '0'+(n / 10));
257 mp_ps_print_char(mp, '0'+(n % 10));
260 @ Conversely, here is a procedure analogous to |print_int|. If the output
261 of this procedure is subsequently read by \MP\ and converted by the
262 |round_decimals| routine above, it turns out that the original value will
263 be reproduced exactly. A decimal point is printed only if the value is
264 not an integer. If there is more than one way to print the result with
265 the optimum number of digits following the decimal point, the closest
266 possible value is given.
268 The invariant relation in the \&{repeat} loop is that a sequence of
269 decimal digits yet to be printed will yield the original number if and only if
270 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
271 We can stop if and only if $f=0$ satisfies this condition; the loop will
272 terminate before $s$ can possibly become zero.
275 static void mp_ps_print_scaled (MP mp,scaled s) {
276 scaled delta; /* amount of allowable inaccuracy */
278 mp_ps_print_char(mp, '-');
279 negate(s); /* print the sign, if negative */
281 mp_ps_print_int(mp, s / unity); /* print the integer part */
285 mp_ps_print_char(mp, '.');
288 s=s+0100000-(delta / 2); /* round the final digit */
289 mp_ps_print_char(mp, '0'+(s / unity));
297 @* \[44a] Dealing with font encodings.
299 First, here are a few helpers for parsing files
301 @d check_buf(size, buf_size)
302 if ((unsigned)(size) > (unsigned)(buf_size)) {
304 mp_snprintf(S,128,"buffer overflow: (%d,%d) at file %s, line %d",
305 size,buf_size, __FILE__, __LINE__ );
306 mp_fatal_error(mp,S);
309 @d append_char_to_buf(c, p, buf, buf_size) do {
312 if (c == 13 || c == EOF)
314 if (c != ' ' || (p > buf && p[-1] != 32)) {
315 check_buf(p - buf + 1, (buf_size));
320 @d append_eol(p, buf, buf_size) do {
321 check_buf(p - buf + 2, (buf_size));
322 if (p - buf > 1 && p[-1] != 10)
324 if (p - buf > 2 && p[-2] == 32) {
331 @d remove_eol(p, buf) do {
337 @d skip(p, c) if (*p == c) p++
338 @d strend(s) strchr(s, 0)
339 @d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
344 boolean loaded; /* the encoding has been loaded? */
345 char *file_name; /* encoding file name */
346 char *enc_name; /* encoding true name */
347 integer objnum; /* object number */
349 integer tounicode; /* object number of associated ToUnicode entry */
359 #define ENC_BUF_SIZE 0x1000
360 char enc_line[ENC_BUF_SIZE];
364 @d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file)
365 @d enc_close() (mp->close_file)(mp,mp->ps->enc_file)
368 static int enc_getchar(MP mp) {
370 unsigned char abyte=0;
371 void *byte_ptr = &abyte;
372 (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len);
377 static boolean mp_enc_open (MP mp, char *n) {
378 mp->ps->enc_file=(mp->open_file)(mp,n, "r", mp_filetype_encoding);
379 if (mp->ps->enc_file!=NULL)
384 static void mp_enc_getline (MP mp) {
389 print_err("unexpected end of file");
392 p = mp->ps->enc_line;
394 c = enc_getchar (mp);
395 append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
397 append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
398 if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
401 static void mp_load_enc (MP mp, char *enc_name,
402 char **enc_encname, char **glyph_names){
403 char buf[ENC_BUF_SIZE], *p, *r;
406 unsigned save_selector = mp->selector;
407 if (!mp_enc_open (mp,enc_name)) {
408 mp_print (mp,"cannot open encoding file for reading");
411 mp_normalize_selector(mp);
413 mp_print (mp, enc_name);
415 if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
416 remove_eol (r, mp->ps->enc_line);
417 print_err ("invalid encoding vector (a name or `[' missing): `");
418 mp_print(mp,mp->ps->enc_line);
422 while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
423 myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
424 memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
425 *(myname+(r-mp->ps->enc_line-1))=0;
426 *enc_encname = myname;
434 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
437 if (names_count > 256) {
438 print_err ("encoding vector contains more than 256 names");
441 if (mp_xstrcmp (buf, notdef) != 0)
442 glyph_names[names_count] = mp_xstrdup (mp,buf);
445 if (*r != 10 && *r != '%') {
446 if (str_prefix (r, "] def"))
449 remove_eol (r, mp->ps->enc_line);
451 ("invalid encoding vector: a name or `] def' expected: `");
452 mp_print(mp,mp->ps->enc_line);
458 r = mp->ps->enc_line;
463 mp->selector = save_selector;
465 static void mp_read_enc (MP mp, enc_entry * e) {
469 mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
473 @ |write_enc| is used to write either external encoding (given in map file) or
474 internal encoding (read from the font file);
475 the 2nd argument is a pointer to the encoding entry;
478 static void mp_write_enc (MP mp, enc_entry * e) {
482 if (e->objnum != 0) /* the encoding has been written already */
487 mp_ps_print(mp,"\n%%%%BeginResource: encoding ");
488 mp_ps_print(mp, e->enc_name);
489 mp_ps_print_nl(mp, "/");
490 mp_ps_print(mp, e->enc_name);
491 mp_ps_print(mp, " [ ");
492 foffset = strlen(e->file_name)+3;
493 for (i = 0; i < 256; i++) {
495 if (s+1+foffset>=80) {
500 mp_ps_print_char(mp,'/');
501 mp_ps_print(mp, g[i]);
502 mp_ps_print_char(mp,' ');
506 mp_ps_print_nl (mp,"] def\n");
507 mp_ps_print(mp,"%%%%EndResource");
511 @ All encoding entries go into AVL tree for fast search by name.
514 struct avl_table *enc_tree;
516 @ Memory management functions for avl
518 @<Static variables in the outer block@>=
519 static const char notdef[] = ".notdef";
522 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
523 static void avl_xfree (struct libavl_allocator *allocator, void *block);
526 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
528 return malloc (size);
530 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
536 struct libavl_allocator avl_xallocator;
538 @ @<Set initial...@>=
539 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
540 mp->ps->avl_xallocator.libavl_free= avl_xfree;
541 mp->ps->enc_tree = NULL;
544 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
546 return strcmp (((const enc_entry *) pa)->file_name,
547 ((const enc_entry *) pb)->file_name);
549 static enc_entry * mp_add_enc (MP mp, char *s) {
553 if (mp->ps->enc_tree == NULL) {
554 mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
557 p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
558 if (p != NULL) /* encoding already registered */
560 p = mp_xmalloc (mp,1,sizeof (enc_entry));
562 p->file_name = mp_xstrdup (mp,s);
565 p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
566 for (i = 0; i < 256; i++)
567 p->glyph_names[i] = (char *) notdef;
568 aa = avl_probe (mp->ps->enc_tree, p);
575 static void mp_destroy_enc_entry (void *pa, void *pb) {
578 p = (enc_entry *) pa;
580 mp_xfree (p->file_name);
581 if (p->glyph_names != NULL)
582 for (i = 0; i < 256; i++)
583 if (p->glyph_names[i] != notdef)
584 mp_xfree (p->glyph_names[i]);
585 mp_xfree (p->glyph_names);
590 static void enc_free (MP mp);
592 @ @c static void enc_free (MP mp) {
593 if (mp->ps->enc_tree != NULL)
594 avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
598 static void mp_reload_encodings (MP mp) ;
599 static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) ;
601 @ @c void mp_reload_encodings (MP mp) {
605 font_number lastfnum = mp->last_fnum;
606 for (f=null_font+1;f<=lastfnum;f++) {
607 if (mp->font_enc_name[f]!=NULL ) {
608 mp_xfree(mp->font_enc_name[f]);
609 mp->font_enc_name[f]=NULL;
611 if (mp_has_fm_entry (mp,f,&fm_cur)) {
612 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
613 e = fm_cur->encoding;
619 static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) {
623 for (f=null_font+1;f<=lastfnum;f++) {
624 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
625 if (fm != NULL && (fm->ps_name != NULL)) {
626 if (is_reencoded (fm)) {
627 if (encodings_only || (!is_subsetted (fm))) {
629 mp_write_enc (mp, e);
630 /* clear for next run */
639 @* \[44b] Parsing font map files.
647 @d fm_close() (mp->close_file)(mp,mp->ps->fm_file)
648 @d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file)
651 static int fm_getchar(MP mp) {
653 unsigned char abyte=0;
654 void *byte_ptr = &abyte;
655 (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
663 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
664 enum _ltype { MAPFILE, MAPLINE };
665 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
666 typedef struct mitem {
667 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
668 int type; /* map file or map line */
669 char *map_line; /* pointer to map file name or map line */
670 int lineno; /* line number in map file */
676 fm_entry *loaded_tfm_found;
677 fm_entry *avail_tfm_found;
678 fm_entry *non_tfm_found;
679 fm_entry *not_avail_tfm_found;
681 @ @<Set initial...@>=
682 mp->ps->mitem = NULL;
685 static const char nontfm[] = "<nontfm>";
688 @d read_field(r, q, buf) do {
690 while (*r != ' ' && *r != '\0')
698 fm->F = mp_xstrdup(mp,buf);
710 static fm_entry *new_fm_entry (MP mp) {
712 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
717 fm->subset_tag = NULL;
719 fm->tfm_num = null_font;
720 fm->tfm_avail = TFM_UNCHECKED;
728 fm->all_glyphs = false;
735 static void delete_fm_entry (fm_entry * fm) {
736 mp_xfree (fm->tfm_name);
737 mp_xfree (fm->ps_name);
738 mp_xfree (fm->ff_name);
739 mp_xfree (fm->subset_tag);
740 mp_xfree (fm->charset);
744 static ff_entry *new_ff_entry (MP mp) {
746 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
752 static void delete_ff_entry (ff_entry * ff) {
753 mp_xfree (ff->ff_name);
754 mp_xfree (ff->ff_path);
758 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
759 static char buf[SMALL_BUF_SIZE];
760 char *p = tfmname, *r = strend (p) - 1, *q = r;
761 while (q > p && mp_isdigit (*q))
763 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
765 check_buf (q - p + 1, SMALL_BUF_SIZE);
766 strncpy (buf, p, (size_t) (q - p));
773 static boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
776 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
777 fm_entry *res = NULL;
778 res = mp_fm_lookup (mp, f);
782 return (res != NULL);
786 struct avl_table *tfm_tree;
787 struct avl_table *ps_tree;
788 struct avl_table *ff_tree;
790 @ @<Set initial...@>=
791 mp->ps->tfm_tree = NULL;
792 mp->ps->ps_tree = NULL;
793 mp->ps->ff_tree = NULL;
795 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
798 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
800 return strcmp (((const fm_entry *) pa)->tfm_name,
801 ((const fm_entry *) pb)->tfm_name);
804 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
806 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
808 const fm_entry *p1 = (const fm_entry *) pa;
809 const fm_entry *p2 = (const fm_entry *) pb;
811 assert (p1->ps_name != NULL && p2->ps_name != NULL);
812 if ((i = strcmp (p1->ps_name, p2->ps_name)))
814 cmp_return (p1->slant, p2->slant);
815 cmp_return (p1->extend, p2->extend);
816 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
817 (i = strcmp (p1->tfm_name, p2->tfm_name)))
822 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
824 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
826 return strcmp (((const ff_entry *) pa)->ff_name,
827 ((const ff_entry *) pb)->ff_name);
830 @ @c static void create_avl_trees (MP mp) {
831 if (mp->ps->tfm_tree == NULL) {
832 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
833 assert (mp->ps->tfm_tree != NULL);
835 if (mp->ps->ps_tree == NULL) {
836 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
837 assert (mp->ps->ps_tree != NULL);
839 if (mp->ps->ff_tree == NULL) {
840 mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
841 assert (mp->ps->ff_tree != NULL);
845 @ The function |avl_do_entry| is not completely symmetrical with regards
846 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
847 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
848 with the original version.
852 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
853 @d set_pslink(fm) ((fm)->links |= LINK_PS)
854 @d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
855 @d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
856 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
857 @d has_pslink(fm) ((fm)->links & LINK_PS)
860 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
866 /* handle |tfm_name| link */
868 if (strcmp (fp->tfm_name, nontfm)) {
869 p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
871 if (mode == FM_DUPIGNORE) {
872 mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
876 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
877 if (mp_has_font_size(mp,p->tfm_num)) {
879 "fontmap entry for `%s' has been used, replace/delete not allowed",
884 a = avl_delete (mp->ps->tfm_tree, p);
891 if (mode != FM_DELETE) {
892 aa = avl_probe (mp->ps->tfm_tree, fp);
898 /* handle |ps_name| link */
900 if (fp->ps_name != NULL) {
901 assert (fp->tfm_name != NULL);
902 p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
904 if (mode == FM_DUPIGNORE) {
906 "ps_name entry for `%s' already exists, duplicates ignored",
910 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
911 if (mp_has_font_size(mp,p->tfm_num)) {
912 /* REPLACE/DELETE not allowed */
914 "fontmap entry for `%s' has been used, replace/delete not allowed",
919 a = avl_delete (mp->ps->ps_tree, p);
922 if (!has_tfmlink (p))
926 if (mode != FM_DELETE) {
927 aa = avl_probe (mp->ps->ps_tree, fp);
933 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
934 return 1; /* deallocation of |fm_entry| structure required */
939 @ consistency check for map entry, with warn flag
942 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
946 if (fm->ps_name != NULL) {
947 if (is_basefont (fm)) {
948 if (is_fontfile (fm) && !is_included (fm)) {
950 mp_snprintf(s,128, "invalid entry for `%s': "
951 "font file must be included or omitted for base fonts",
957 } else { /* not a base font */
958 /* if no font file given, drop this entry */
959 /* |if (!is_fontfile (fm)) {
962 "invalid entry for `%s': font file missing",
971 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
974 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
980 if ((fm->slant != 0 || fm->extend != 0) &&
981 (is_truetype (fm))) {
984 "invalid entry for `%s': "
985 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
991 if (abs (fm->slant) > 1000) {
994 "invalid entry for `%s': too big value of SlantFont (%d/1000.0)",
995 fm->tfm_name, (int)fm->slant);
1000 if (abs (fm->extend) > 2000) {
1003 "invalid entry for `%s': too big value of ExtendFont (%d/1000.0)",
1004 fm->tfm_name, (int)fm->extend);
1009 if (fm->pid != -1 &&
1010 !(is_truetype (fm) && is_included (fm) &&
1011 is_subsetted (fm) && !is_reencoded (fm))) {
1014 "invalid entry for `%s': "
1015 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
1024 @ returns true if s is one of the 14 std. font names; speed-trimmed.
1026 @c static boolean check_basefont (char *s) {
1027 static const char *basefont_names[] = {
1028 "Courier", /* 0:7 */
1029 "Courier-Bold", /* 1:12 */
1030 "Courier-Oblique", /* 2:15 */
1031 "Courier-BoldOblique", /* 3:19 */
1032 "Helvetica", /* 4:9 */
1033 "Helvetica-Bold", /* 5:14 */
1034 "Helvetica-Oblique", /* 6:17 */
1035 "Helvetica-BoldOblique", /* 7:21 */
1037 "Times-Roman", /* 9:11 */
1038 "Times-Bold", /* 10:10 */
1039 "Times-Italic", /* 11:12 */
1040 "Times-BoldItalic", /* 12:16 */
1041 "ZapfDingbats" /* 13:12 */
1043 static const int Index[] =
1044 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1047 const size_t n = strlen (s);
1051 if (n == 12) { /* three names have length 12 */
1054 k = 1; /* Courier-Bold */
1057 k = 11; /* Times-Italic */
1060 k = 13; /* ZapfDingbats */
1067 if (k > -1 && !strcmp (basefont_names[k], s))
1073 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1075 @c static void fm_scan_line (MP mp) {
1076 int a, b, c, j, u = 0, v = 0;
1079 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1080 char *p, *q, *r, *s;
1082 switch (mp->ps->mitem->type) {
1086 c = fm_getchar (mp);
1087 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1093 r = mp->ps->mitem->map_line;
1098 if (*r == '\0' || is_cfg_comment (*r))
1100 fm = new_fm_entry (mp);
1101 read_field (r, q, buf);
1102 set_field (tfm_name);
1104 read_field (r, q, buf);
1105 if (*buf != '<' && *buf != '"')
1106 set_field (ps_name);
1108 r = p; /* unget the field */
1109 if (mp_isdigit (*r)) { /* font flags given */
1110 fm->flags = atoi (r);
1111 while (mp_isdigit (*r))
1114 while (1) { /* loop through "specials", encoding, font file */
1119 case '"': /* opening quote */
1124 if (sscanf (r, "%f %n", &d, &j) > 0) {
1125 s = r + j; /* jump behind number, eat also blanks, if any */
1126 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1127 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1128 if (str_prefix (s, "SlantFont")) {
1129 d *= 1000.0; /* correct rounding also for neg. numbers */
1130 fm->slant = (short int) (d > 0 ? d + 0.5 : d - 0.5);
1131 r = s + strlen ("SlantFont");
1132 } else if (str_prefix (s, "ExtendFont")) {
1134 fm->extend = (short int) (d > 0 ? d + 0.5 : d - 0.5);
1135 if (fm->extend == 1000)
1137 r = s + strlen ("ExtendFont");
1138 } else { /* unknown name */
1140 *r != ' ' && *r != '"' && *r != '\0';
1141 r++); /* jump over name */
1142 c = *r; /* remember char for temporary end of string */
1144 mp_snprintf(warn_s,128,
1145 "invalid entry for `%s': unknown name `%s' ignored",
1151 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1154 if (*r == '"') /* closing quote */
1157 mp_snprintf(warn_s,128,
1158 "invalid entry for `%s': closing quote missing",
1164 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1165 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1166 fm->pid = (short int)a;
1167 fm->eid = (short int)b;
1171 default: /* encoding or font file specification */
1175 if (*r == '<' || *r == '[')
1178 read_field (r, q, buf);
1179 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1180 if (strlen (buf) > 4 && mp_strcasecmp (strend (buf) - 4, ".enc") == 0) {
1181 fm->encoding = mp_add_enc (mp, buf);
1182 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1183 } else if (strlen (buf) > 0) { /* file name given */
1184 /* font file, formats:
1185 * subsetting: '<cmr10.pfa'
1186 * no subsetting: '<<cmr10.pfa'
1187 * no embedding: 'cmr10.pfa'
1189 if (a == '<' || u == '<') {
1191 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1193 /* otherwise b == '<' (or '[') => no subsetting */
1195 set_field (ff_name);
1204 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1206 if (is_fontfile (fm)
1207 && mp_strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1209 if (check_fm_entry (mp,fm, true) != 0)
1212 Until here the map line has been completely scanned without errors;
1213 fm points to a valid, freshly filled-out |fm_entry| structure.
1214 Now follows the actual work of registering/deleting.
1216 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
1219 delete_fm_entry (fm);
1223 @c static void fm_read_info (MP mp) {
1226 if (mp->ps->tfm_tree == NULL)
1227 create_avl_trees (mp);
1228 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1230 mp->ps->mitem->lineno = 1;
1231 switch (mp->ps->mitem->type) {
1233 n = mp->ps->mitem->map_line;
1234 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1235 if (!mp->ps->fm_file) {
1236 mp_snprintf(s,256,"cannot open font map file %s",n);
1239 unsigned save_selector = mp->selector;
1240 mp_normalize_selector(mp);
1243 while (!fm_eof ()) {
1245 mp->ps->mitem->lineno++;
1249 mp->selector = save_selector;
1250 mp->ps->fm_file = NULL;
1260 mp->ps->mitem->map_line = NULL; /* done with this line */
1264 @ @c static void init_fm (fm_entry * fm, font_number f) {
1265 if (fm->tfm_num == null_font ) {
1267 fm->tfm_avail = TFM_FOUND;
1272 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1275 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1280 if (mp->ps->tfm_tree == NULL)
1281 fm_read_info (mp); /* only to read default map file */
1282 tfm = mp->font_name[f];
1283 assert (strcmp (tfm, nontfm));
1284 /* Look up for full <tfmname>[+-]<expand> */
1286 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1289 return (fm_entry *) fm;
1291 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1292 if (tfm == NULL) /* not an expanded font, nothing to do */
1296 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1297 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1298 return (fm_entry *) fm; /* font expansion uses the base font */
1303 @ Early check whether a font file exists. Used e. g. for replacing fonts
1304 of embedded PDF files: Without font file, the font within the embedded
1305 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1306 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1307 only once per font file name + expansion parameter. This might help
1308 keeping speed, if many PDF pages with same fonts are to be embedded.
1310 The |ff_tree| contains only font files, which are actually needed,
1311 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1314 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1319 assert (fm->ff_name != NULL);
1320 tmp.ff_name = fm->ff_name;
1321 ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1322 if (ff == NULL) { /* not yet in database */
1323 ff = new_ff_entry (mp);
1324 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1325 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1326 aa = avl_probe (mp->ps->ff_tree, ff);
1327 assert (aa != NULL);
1332 @ Process map file given by its name or map line contents. Items not
1333 beginning with [+-=] flush default map file, if it has not yet been
1334 read. Leading blanks and blanks immediately following [+-=] are ignored.
1337 @c static void mp_process_map_item (MP mp, char *s, int type) {
1341 s++; /* ignore leading blank */
1343 case '+': /* +mapfile.map, +mapline */
1344 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1347 case '=': /* =mapfile.map, =mapline */
1348 mode = FM_REPLACE; /* try to replace earlier entry */
1351 case '-': /* -mapfile.map, -mapline */
1352 mode = FM_DELETE; /* try to delete entry */
1356 mode = FM_DUPIGNORE; /* like +, but also: */
1357 mp_xfree(mp->ps->mitem->map_line);
1358 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1361 s++; /* ignore blank after [+-=] */
1362 p = s; /* map item starts here */
1364 case MAPFILE: /* remove blank at end */
1365 while (*p != '\0' && *p != ' ')
1369 case MAPLINE: /* blank at end allowed */
1374 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1376 if (*s != '\0') { /* only if real item to process */
1377 mp->ps->mitem->mode = mode;
1378 mp->ps->mitem->type = type;
1379 mp->ps->mitem->map_line = s;
1384 @ @<Exported function headers@>=
1385 void mp_map_file (MP mp, str_number t);
1386 void mp_map_line (MP mp, str_number t);
1387 void mp_init_map_file (MP mp, int is_troff);
1390 void mp_map_file (MP mp, str_number t) {
1391 char *s = mp_xstrdup(mp,mp_str (mp,t));
1392 mp_process_map_item (mp, s, MAPFILE);
1395 void mp_map_line (MP mp, str_number t) {
1396 char *s = mp_xstrdup(mp,mp_str (mp,t));
1397 mp_process_map_item (mp, s, MAPLINE);
1402 @c void mp_init_map_file (MP mp, int is_troff) {
1404 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1405 mp->ps->mitem->mode = FM_DUPIGNORE;
1406 mp->ps->mitem->type = MAPFILE;
1407 mp->ps->mitem->map_line = NULL;
1408 r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap);
1411 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1414 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1416 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1421 @ @<Dealloc variables@>=
1422 if (mp->ps->mitem!=NULL) {
1423 mp_xfree(mp->ps->mitem->map_line);
1424 mp_xfree(mp->ps->mitem);
1430 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1433 fm = (fm_entry *) pa;
1434 if (!has_pslink (fm))
1435 delete_fm_entry (fm);
1439 static void destroy_fm_entry_ps (void *pa, void *pb) {
1442 fm = (fm_entry *) pa;
1443 if (!has_tfmlink (fm))
1444 delete_fm_entry (fm);
1448 static void destroy_ff_entry (void *pa, void *pb) {
1451 ff = (ff_entry *) pa;
1452 delete_ff_entry (ff);
1456 static void fm_free (MP mp);
1459 static void fm_free (MP mp) {
1460 if (mp->ps->tfm_tree != NULL)
1461 avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1462 if (mp->ps->ps_tree != NULL)
1463 avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1464 if (mp->ps->ff_tree != NULL)
1465 avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1468 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1469 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1470 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
1471 one or more spaces, a PostScript name, and possibly a space and some other junk.
1472 This routine reads the table, updates |font_ps_name| entries starting after
1473 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
1475 @d ps_tab_name "psfonts.map" /* locates font name translation table */
1478 static void mp_read_psname_table (MP mp) ;
1481 static void mp_read_psname_table (MP mp) {
1483 if (mp->ps->mitem == NULL) {
1484 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1485 mp->ps->mitem->mode = FM_DUPIGNORE;
1486 mp->ps->mitem->type = MAPFILE;
1487 mp->ps->mitem->map_line = NULL;
1489 mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name);
1491 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1492 if (mp_has_fm_entry(mp, k, NULL)) {
1493 mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1496 mp->last_ps_fnum=mp->last_fnum;
1500 @ The traditional function is a lot shorter now.
1504 @* \[44c] Helper functions for Type1 fonts.
1507 typedef char char_entry;
1508 typedef unsigned char Byte;
1512 char_entry *char_ptr, *char_array;
1514 char *job_id_string;
1516 @ @<Set initial...@>=
1517 mp->ps->char_array = NULL;
1518 mp->ps->job_id_string = NULL;
1521 @d SMALL_ARRAY_SIZE 256
1525 void mp_set_job_id (MP mp) {
1526 char *name_string, *format_string, *s;
1528 if (mp->ps->job_id_string != NULL)
1530 if ( mp->job_name==NULL )
1531 mp->job_name = mp_xstrdup(mp,"mpout");
1532 name_string = mp_xstrdup (mp,mp->job_name);
1533 format_string = mp_xstrdup (mp,mp->mem_ident);
1534 slen = SMALL_BUF_SIZE +
1535 strlen (name_string) +
1536 strlen (format_string);
1537 s = mp_xmalloc (mp,slen, sizeof (char));
1538 @= /*@@-bufferoverflowhigh@@*/ @>
1539 sprintf (s,"%.4u/%.2u/%.2u %.2u:%.2u %s %s",
1540 ((unsigned)mp->internal[mp_year]>>16),
1541 ((unsigned)mp->internal[mp_month]>>16),
1542 ((unsigned)mp->internal[mp_day]>>16),
1543 ((unsigned)mp->internal[mp_time]>>16) / 60,
1544 ((unsigned)mp->internal[mp_time]>>16) % 60,
1545 name_string, format_string);
1546 @= /*@@=bufferoverflowhigh@@*/ @>
1547 mp->ps->job_id_string = mp_xstrdup (mp,s);
1549 mp_xfree (name_string);
1550 mp_xfree (format_string);
1552 static void fnstr_append (MP mp, const char *s) {
1553 size_t l = strlen (s) + 1;
1554 alloc_array (char, l, SMALL_ARRAY_SIZE);
1555 strcat (mp->ps->char_ptr, s);
1556 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1559 @ @<Exported function headers@>=
1560 void mp_set_job_id (MP mp) ;
1562 @ @<Dealloc variables@>=
1563 mp_xfree(mp->ps->job_id_string);
1565 @ this is not really a true crc32, but it should be just enough to keep
1566 subsets prefixes somewhat disjunct
1569 static unsigned long crc32 (unsigned long oldcrc, const Byte *buf, size_t len) {
1570 unsigned long ret = 0;
1573 ret = (unsigned long)((23<<24)+(45<<16)+(67<<8)+89);
1576 ret = (ret<<2)+buf[i];
1579 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1580 integer b; /* |char_base[f]| */
1582 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1588 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, font_number tex_font)
1594 if (mp->ps->job_id_string ==NULL)
1595 mp_fatal_error(mp, "no job id!");
1596 l = strlen (mp->ps->job_id_string) + 1;
1598 alloc_array (char, l, SMALL_ARRAY_SIZE);
1599 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1600 mp->ps->char_ptr = strend (mp->ps->char_array);
1601 if (fm_cur->tfm_name != NULL) {
1602 fnstr_append (mp," TFM name: ");
1603 fnstr_append (mp,fm_cur->tfm_name);
1605 fnstr_append (mp," PS name: ");
1606 if (fm_cur->ps_name != NULL)
1607 fnstr_append (mp,fm_cur->ps_name);
1608 fnstr_append (mp," Encoding: ");
1609 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1610 fnstr_append (mp,(fm_cur->encoding)->file_name);
1612 fnstr_append (mp,"built-in");
1613 fnstr_append (mp," CharSet: ");
1614 for (i = 0; i < 256; i++)
1615 if (mp_char_marked (mp,tex_font, (eight_bits)i) && glyph_names[i] != notdef) {
1616 if (glyph_names[i]!=NULL) {
1617 fnstr_append (mp,"/");
1618 fnstr_append (mp,glyph_names[i]);
1621 if (fm_cur->charset != NULL) {
1622 fnstr_append (mp," Extra CharSet: ");
1623 fnstr_append (mp, fm_cur->charset);
1625 crc = crc32 (0L, Z_NULL, 0);
1626 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1627 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1628 * there are 26 uppercase chars ==> each char represents a number in range
1629 * |0..25|. The maximal number that can be represented by the tag is
1630 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1631 * of the CRC must be dropped out.
1633 for (i = 0; i < 6; i++) {
1634 tag[i] = 'A' + crc % 26;
1638 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1644 @d external_enc() (fm_cur->encoding)->glyph_names
1645 @d is_used_char(c) mp_char_marked (mp, tex_font, (eight_bits)c)
1646 @d end_last_eexec_line()
1647 mp->ps->hexline_length = HEXLINE_WIDTH;
1649 mp->ps->t1_eexec_encrypt = false
1650 @d t1_log(s) mp_print(mp,(char *)s)
1651 @d t1_putchar(c) wps_chr(c)
1652 @d embed_all_glyphs(tex_font) false
1654 @d extra_charset() mp->ps->dvips_extra_charset
1655 @d update_subset_tag()
1656 @d fixedcontent true
1659 #define PRINTF_BUF_SIZE 1024
1660 char *dvips_extra_charset;
1662 unsigned char *grid;
1663 char *ext_glyph_names[256];
1664 char print_buf[PRINTF_BUF_SIZE];
1665 int t1_byte_waiting;
1667 @ @<Set initial ...@>=
1668 mp->ps->dvips_extra_charset=NULL;
1669 mp->ps->t1_byte_waiting=0;
1672 @d t1_ungetchar(A) mp->ps->t1_byte_waiting=A
1673 @d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file)
1674 @d t1_close() (mp->close_file)(mp,mp->ps->t1_file)
1675 @d valid_code(c) (c >= 0 && c < 256)
1678 static int t1_getchar (MP mp) {
1681 void *byte_ptr = &abyte;
1682 if (mp->ps->t1_byte_waiting) {
1683 abyte = mp->ps->t1_byte_waiting;
1684 mp->ps->t1_byte_waiting = 0;
1686 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
1691 @ @<Static variables in the outer block@>=
1692 static const char *standard_glyph_names[256] =
1693 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1694 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1695 notdef, notdef, notdef, notdef, notdef, notdef,
1696 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1697 "space", "exclam", "quotedbl", "numbersign",
1698 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1699 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1700 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1701 "eight", "nine", "colon", "semicolon", "less",
1702 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1703 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1704 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1705 "backslash", "bracketright", "asciicircum", "underscore",
1706 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1707 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1708 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1709 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1710 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1711 notdef, notdef, notdef, notdef, notdef, notdef,
1712 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1713 notdef, notdef, notdef, "exclamdown", "cent",
1714 "sterling", "fraction", "yen", "florin", "section", "currency",
1715 "quotesingle", "quotedblleft", "guillemotleft",
1716 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1717 "dagger", "daggerdbl", "periodcentered", notdef,
1718 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1719 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1720 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1721 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1722 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1723 notdef, notdef, notdef, notdef, notdef, notdef,
1724 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1725 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1726 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1727 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1728 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1729 "germandbls", notdef, notdef, notdef, notdef };
1730 static const char charstringname[] = "/CharStrings";
1733 char **t1_glyph_names;
1734 char *t1_builtin_glyph_names[256];
1735 char charsetstr[0x4000];
1736 boolean read_encoding_only;
1740 #define T1_BUF_SIZE 0x10
1744 #define CS_VMOVETO 4
1745 #define CS_RLINETO 5
1746 #define CS_HLINETO 6
1747 #define CS_VLINETO 7
1748 #define CS_RRCURVETO 8
1749 #define CS_CLOSEPATH 9
1750 #define CS_CALLSUBR 10
1751 #define CS_RETURN 11
1752 #define CS_ESCAPE 12
1754 #define CS_ENDCHAR 14
1755 #define CS_RMOVETO 21
1756 #define CS_HMOVETO 22
1757 #define CS_VHCURVETO 30
1758 #define CS_HVCURVETO 31
1759 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1761 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1762 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1763 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1764 #define CS_SEAC CS_1BYTE_MAX + 6
1765 #define CS_SBW CS_1BYTE_MAX + 7
1766 #define CS_DIV CS_1BYTE_MAX + 12
1767 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1768 #define CS_POP CS_1BYTE_MAX + 17
1769 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1770 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1771 #define CS_MAX CS_2BYTE_MAX
1774 typedef unsigned char byte;
1776 byte nargs; /* number of arguments */
1777 boolean bottom; /* take arguments from bottom of stack? */
1778 boolean clear; /* clear stack? */
1780 } cc_entry; /* CharString Command */
1782 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1784 unsigned short len; /* length of the whole string */
1785 unsigned short cslen; /* length of the encoded part of the string */
1795 unsigned short t1_dr, t1_er;
1796 unsigned short t1_cslen;
1800 typedef char t1_line_entry;
1801 typedef char t1_buf_entry;
1804 t1_line_entry *t1_line_ptr, *t1_line_array;
1805 size_t t1_line_limit;
1806 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1807 size_t t1_buf_limit;
1809 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1810 char *cs_dict_start, *cs_dict_end;
1811 int cs_count, cs_size, cs_size_pos;
1813 char *subr_array_start, *subr_array_end;
1814 int subr_max, subr_size, subr_size_pos;
1816 @ @<Set initial...@>=
1817 mp->ps->t1_line_array = NULL;
1818 mp->ps->t1_buf_array = NULL;
1821 This list contains the begin/end tokens commonly used in the
1822 /Subrs array of a Type 1 font.
1824 @<Static variables in the outer block@>=
1825 static const char *cs_token_pairs_list[][2] = {
1828 {" RD", "noaccess put"},
1829 {" -|", "noaccess put"},
1834 const char **cs_token_pair;
1835 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1836 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1837 int t1_block_length;
1845 @<Set initial ...@>=
1846 mp->ps->hexline_length = 0;
1849 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1850 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1851 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1852 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1853 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1854 @d t1_subrs() t1_prefix("/Subrs")
1855 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1856 @d t1_cleartomark() t1_prefix("cleartomark")
1859 static void end_hexline (MP mp) {
1860 if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
1862 mp->ps->hexline_length = 0;
1865 static void t1_check_pfa (MP mp) {
1866 const int c = t1_getchar (mp);
1867 mp->ps->t1_pfa = (c != 128) ? true : false;
1870 static int t1_getbyte (MP mp)
1872 int c = t1_getchar (mp);
1875 if (mp->ps->t1_block_length == 0) {
1877 mp_fatal_error (mp, "invalid marker");
1878 c = t1_getchar (mp);
1881 (void)t1_getchar (mp);
1884 mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
1885 mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 8;
1886 mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 16;
1887 mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 24;
1888 c = t1_getchar (mp);
1890 mp->ps->t1_block_length--;
1893 static int hexval (int c) {
1894 if (c >= 'A' && c <= 'F')
1895 return c - 'A' + 10;
1896 else if (c >= 'a' && c <= 'f')
1897 return c - 'a' + 10;
1898 else if (c >= '0' && c <= '9')
1903 static byte edecrypt (MP mp, byte cipher) {
1905 if (mp->ps->t1_pfa) {
1906 while (cipher == 10 || cipher == 13)
1907 cipher = (byte)t1_getbyte (mp);
1908 mp->ps->last_hexbyte = cipher = (byte)(((byte)hexval (cipher) << 4) +
1909 hexval (t1_getbyte (mp)));
1911 plain = (cipher ^ (mp->ps->t1_dr >> 8));
1912 mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * t1_c1 + t1_c2;
1915 static byte cdecrypt (byte cipher, unsigned short *cr)
1917 const byte plain = (cipher ^ (*cr >> 8));
1918 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1921 static byte eencrypt (MP mp, byte plain)
1923 const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1924 mp->ps->t1_er = (cipher + mp->ps->t1_er) * t1_c1 + t1_c2;
1928 static byte cencrypt (byte plain, unsigned short *cr)
1930 const byte cipher = (plain ^ (*cr >> 8));
1931 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1935 static char *eol (char *s) {
1936 char *p = strend (s);
1937 if (p!=NULL && p - s > 1 && p[-1] != 10) {
1943 static float t1_scan_num (MP mp, char *p, char **r)
1948 if (sscanf (p, "%g", &f) != 1) {
1949 remove_eol (p, mp->ps->t1_line_array);
1950 mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1951 mp_fatal_error(mp,s);
1954 for (; mp_isdigit (*p) || *p == '.' ||
1955 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1961 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1964 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1967 while (s1 >= begin_buf && s2 >= s) {
1976 @d alloc_array(T, n, s) do {
1977 if (mp->ps->T##_array == NULL) {
1978 mp->ps->T##_limit = (s);
1979 if ((size_t)(n) > mp->ps->T##_limit)
1980 mp->ps->T##_limit = (size_t)(n);
1981 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
1982 mp->ps->T##_ptr = mp->ps->T##_array;
1984 else if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
1985 size_t last_ptr_index;
1986 last_ptr_index = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array);
1987 mp->ps->T##_limit *= 2;
1988 if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
1989 mp->ps->T##_limit = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n));
1990 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
1991 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
1995 @d out_eexec_char(A) t1_outhex(mp,(A))
1998 static void t1_outhex (MP mp, byte b)
2000 static const char *hexdigits = "0123456789ABCDEF";
2001 t1_putchar (hexdigits[b / 16]);
2002 t1_putchar (hexdigits[b % 16]);
2003 mp->ps->hexline_length += 2;
2006 static void t1_getline (MP mp) {
2007 int c, l, eexec_scan;
2009 static const char eexec_str[] = "currentfile eexec";
2010 static int eexec_len = 17; /* |strlen(eexec_str)| */
2013 mp_fatal_error (mp,"unexpected end of file");
2014 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2015 alloc_array (t1_line, 1, T1_BUF_SIZE);
2016 mp->ps->t1_cslen = 0;
2018 c = t1_getbyte (mp);
2021 while (!t1_eof ()) {
2022 if (mp->ps->t1_in_eexec == 1)
2023 c = edecrypt (mp,(byte)c);
2024 alloc_array (t1_line, 1, T1_BUF_SIZE);
2025 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2026 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
2027 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
2032 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2034 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
2035 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2036 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2037 p = mp->ps->t1_line_ptr - 5;
2040 l = (int)t1_scan_num (mp, p + 1, 0);
2041 mp->ps->t1_cslen = (unsigned short)l;
2042 mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2043 /* |mp->ps->cs_start| is an index now */
2044 alloc_array (t1_line, l, T1_BUF_SIZE);
2046 *mp->ps->t1_line_ptr = (t1_line_entry)edecrypt (mp,(byte)t1_getbyte (mp));
2047 mp->ps->t1_line_ptr++;
2050 c = t1_getbyte (mp);
2052 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
2053 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2054 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2056 if (eexec_scan == eexec_len)
2057 mp->ps->t1_in_eexec = 1;
2059 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2060 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2061 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2064 static void t1_putline (MP mp)
2066 char *p = mp->ps->t1_line_array;
2067 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2069 if (mp->ps->t1_eexec_encrypt) {
2070 while (p < mp->ps->t1_line_ptr)
2071 out_eexec_char (eencrypt (mp,(byte)*p++));
2073 while (p < mp->ps->t1_line_ptr)
2078 static void t1_puts (MP mp, const char *s)
2080 if (s != mp->ps->t1_line_array)
2081 strcpy (mp->ps->t1_line_array, s);
2082 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2086 static void t1_init_params (MP mp, const char *open_name_prefix,
2087 char *cur_file_name) {
2088 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2089 t1_log (open_name_prefix);
2090 t1_log (cur_file_name);
2092 mp->ps->t1_lenIV = 4;
2093 mp->ps->t1_dr = 55665;
2094 mp->ps->t1_er = 55665;
2095 mp->ps->t1_in_eexec = 0;
2096 mp->ps->t1_cs = false;
2097 mp->ps->t1_scan = true;
2098 mp->ps->t1_synthetic = false;
2099 mp->ps->t1_eexec_encrypt = false;
2100 mp->ps->t1_block_length = 0;
2103 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2104 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2105 t1_log (close_name_suffix);
2110 static void t1_check_block_len (MP mp, boolean decrypt) {
2113 if (mp->ps->t1_block_length == 0)
2115 c = t1_getbyte (mp);
2117 c = edecrypt (mp,(byte)c);
2118 l = mp->ps->t1_block_length;
2119 if (!(l == 0 && (c == 10 || c == 13))) {
2120 mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2123 (void)t1_getbyte (mp);
2126 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2128 if (!mp->ps->t1_pfa)
2129 t1_check_block_len (mp, false);
2130 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2131 (void)edecrypt (mp, (byte)t1_getbyte (mp));
2132 *mp->ps->t1_line_ptr++ = 0;
2134 mp->ps->t1_eexec_encrypt = true;
2135 if (!mp->ps->read_encoding_only)
2136 if (is_included (fm_cur))
2137 t1_putline (mp); /* to put the first four bytes */
2139 static void t1_stop_eexec (MP mp) {
2141 end_last_eexec_line ();
2142 if (!mp->ps->t1_pfa)
2143 t1_check_block_len (mp,true);
2145 c = edecrypt (mp, (byte)t1_getbyte (mp));
2146 if (!(c == 10 || c == 13)) {
2147 if (mp->ps->last_hexbyte == 0)
2150 mp_warn (mp,"unexpected data after eexec");
2153 mp->ps->t1_cs = false;
2154 mp->ps->t1_in_eexec = 2;
2156 static void t1_modify_fm (MP mp) {
2157 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2160 static void t1_modify_italic (MP mp) {
2161 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2166 const char *pdfname;
2176 static key_entry font_keys[FONT_KEYS_NUM] = {
2177 {"Ascent", "Ascender", 0, false},
2178 {"CapHeight", "CapHeight", 0, false},
2179 {"Descent", "Descender", 0, false},
2180 {"FontName", "FontName", 0, false},
2181 {"ItalicAngle", "ItalicAngle", 0, false},
2182 {"StemV", "StdVW", 0, false},
2183 {"XHeight", "XHeight", 0, false},
2184 {"FontBBox", "FontBBox", 0, false},
2196 @d ITALIC_ANGLE_CODE 4
2202 @d FONTBBOX4_CODE 10
2203 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2206 static void t1_scan_keys (MP mp, font_number tex_font,fm_entry *fm_cur) {
2210 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2211 if (t1_prefix ("/FontMatrix")) {
2215 if (t1_prefix ("/ItalicAngle")) {
2216 t1_modify_italic (mp);
2220 if (t1_prefix ("/FontType")) {
2221 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2222 if ((i = (int)t1_scan_num (mp,p, 0)) != 1) {
2224 mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2225 mp_fatal_error(mp,s);
2229 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2230 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2232 if (key - font_keys == MAX_KEY_CODE)
2235 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2237 if ((k = key - font_keys) == FONTNAME_CODE) {
2240 remove_eol (p, mp->ps->t1_line_array);
2241 mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2242 mp_fatal_error(mp,s);
2244 r = ++p; /* skip the slash */
2245 if (is_included (fm_cur)) {
2246 /* save the fontname */
2247 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2248 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2249 mp->ps->fontname_buf[i]=0;
2251 if(is_subsetted (fm_cur)) {
2252 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2253 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2255 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2257 alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
2259 strncpy (r, fm_cur->subset_tag , 6);
2261 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2262 mp->ps->t1_line_ptr = eol (r);
2264 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2266 mp->ps->t1_line_ptr = eol (r);
2271 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2272 && (*p == '[' || *p == '{'))
2274 if (k == FONTBBOX1_CODE) {
2275 for (i = 0; i < 4; i++) {
2276 key[i].value = t1_scan_num (mp, p, &r);
2281 key->value = t1_scan_num (mp, p, 0);
2283 static void t1_scan_param (MP mp, font_number tex_font,fm_entry *fm_cur)
2285 static const char *lenIV = "/lenIV";
2286 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2288 if (t1_prefix (lenIV)) {
2289 mp->ps->t1_lenIV = (short int)t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2292 t1_scan_keys (mp, tex_font,fm_cur);
2294 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2295 if (glyph_names[b] != notdef) {
2296 mp_xfree (glyph_names[b]);
2297 glyph_names[b] = (char *) notdef;
2299 if (glyph_names[a] != notdef) {
2300 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2303 static void t1_builtin_enc (MP mp) {
2304 int i, a, b, c, counter = 0;
2307 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2309 if (t1_suffix ("def")) { /* predefined encoding */
2310 (void)sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2311 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2312 for (i = 0; i < 256; i++)
2313 if (standard_glyph_names[i] == notdef)
2314 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2316 mp->ps->t1_builtin_glyph_names[i] =
2317 mp_xstrdup (mp,standard_glyph_names[i]);
2318 mp->ps->t1_encoding = ENC_STANDARD;
2321 mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2322 mp->ps->t1_buf_array);
2323 mp_fatal_error(mp,s);
2327 mp->ps->t1_encoding = ENC_BUILTIN;
2329 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2330 * not a predefined encoding
2332 * We have two possible forms of Encoding vector. The first case is
2334 * /Encoding [/a /b /c...] readonly def
2336 * and the second case can look like
2338 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2344 for (i = 0; i < 256; i++)
2345 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2346 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2347 r = strchr (mp->ps->t1_line_array, '[') + 1;
2351 for (p = mp->ps->t1_buf_array, r++;
2352 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2356 if (counter > 255) {
2358 (mp, "encoding vector contains more than 256 names");
2360 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2361 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2364 if (*r != 10 && *r != '%') {
2365 if (str_prefix (r, "] def")
2366 || str_prefix (r, "] readonly def"))
2370 remove_eol (r, mp->ps->t1_line_array);
2371 mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2372 mp->ps->t1_line_array);
2373 mp_fatal_error(mp,s);
2377 r = mp->ps->t1_line_array;
2379 } else { /* the second case */
2380 p = strchr (mp->ps->t1_line_array, 10);
2384 p = mp->ps->t1_line_array;
2387 check for `dup <index> <glyph> put'
2389 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2390 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2391 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2392 mp->ps->t1_builtin_glyph_names[i] =
2393 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2394 p = strstr (p, " put") + strlen (" put");
2398 check for `dup dup <to> exch <from> get put'
2400 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2401 && valid_code (a) && valid_code (b)) {
2402 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2403 p = strstr (p, " get put") + strlen (" get put");
2407 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2410 (p, "dup dup %i %i getinterval %i exch putinterval",
2411 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2412 && valid_code (c)) {
2413 for (i = 0; i < c; i++)
2414 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2415 p = strstr (p, " putinterval") + strlen (" putinterval");
2419 check for `def' or `readonly def'
2421 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2422 && strcmp (p, "def\n") == 0)
2425 skip an unrecognizable word
2428 while (*p != ' ' && *p != 10)
2436 static void t1_check_end (MP mp) {
2440 if (t1_prefix ("{restore}"))
2446 char *ff_name; /* base name of font file */
2447 char *ff_path; /* full path to font file */
2451 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2453 ff = check_ff_exist (mp, fm_cur);
2454 if (ff->ff_path != NULL) {
2455 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font);
2457 mp_warn (mp, "cannot open Type 1 font file for reading");
2460 t1_init_params (mp,open_name_prefix,fm_cur->ff_name);
2461 mp->ps->fontfile_found = true;
2465 static void t1_scan_only (MP mp, font_number tex_font, fm_entry *fm_cur) {
2468 t1_scan_param (mp,tex_font, fm_cur);
2470 while (mp->ps->t1_in_eexec == 0);
2471 t1_start_eexec (mp,fm_cur);
2474 t1_scan_param (mp,tex_font, fm_cur);
2476 while (!(t1_charstrings () || t1_subrs ()));
2479 static void t1_include (MP mp, font_number tex_font, fm_entry *fm_cur) {
2482 t1_scan_param (mp,tex_font, fm_cur);
2485 while (mp->ps->t1_in_eexec == 0);
2486 t1_start_eexec (mp,fm_cur);
2489 t1_scan_param (mp,tex_font, fm_cur);
2492 while (!(t1_charstrings () || t1_subrs ()));
2493 mp->ps->t1_cs = true;
2498 while (!t1_end_eexec ());
2500 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2505 while (!t1_cleartomark ());
2506 t1_check_end (mp); /* write "{restore}if" if found */
2511 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2513 mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2514 mp_fatal_error(mp,s);
2518 static const char **check_cs_token_pair (MP mp) {
2519 const char **p = (const char **) cs_token_pairs_list;
2520 for (; p[0] != NULL; ++p)
2521 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2526 static void cs_store (MP mp, boolean is_subr) {
2530 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2531 *mp->ps->t1_buf_ptr++ = *p++);
2532 *mp->ps->t1_buf_ptr = 0;
2534 subr = (int)t1_scan_num (mp, p + 1, 0);
2536 ptr = mp->ps->subr_tab + subr;
2538 ptr = mp->ps->cs_ptr++;
2539 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2541 mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2542 mp_fatal_error(mp,s);
2544 if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
2545 ptr->glyph_name = (char *) notdef;
2547 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2549 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2550 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2551 (size_t) (mp->ps->t1_cslen + 4));
2552 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2553 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2554 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2555 *mp->ps->t1_buf_ptr++ = 10;
2556 if (is_subr && mp->ps->cs_token_pair == NULL)
2557 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2558 ptr->len = (unsigned short)(mp->ps->t1_buf_ptr - mp->ps->t1_buf_array);
2559 ptr->cslen = mp->ps->t1_cslen;
2560 ptr->data = mp_xmalloc (mp, (size_t)ptr->len , sizeof (byte));
2561 memcpy (ptr->data, mp->ps->t1_buf_array, (size_t)ptr->len);
2565 #define store_subr(mp) cs_store(mp,true)
2566 #define store_cs(mp) cs_store(mp,false)
2568 #define CC_STACK_SIZE 24
2570 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2571 static cc_entry cc_tab[CS_MAX];
2572 static boolean is_cc_init = false;
2576 if (stack_ptr - cc_stack < (N)) \
2580 #define stack_error(N) { \
2582 mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2583 (int) N, (int)(stack_ptr - cc_stack)); \
2589 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2591 #define cc_push(V) *stack_ptr++ = V
2592 #define cc_clear() stack_ptr = cc_stack
2594 #define set_cc(N, B, A, C) \
2595 cc_tab[N].nargs = A; \
2596 cc_tab[N].bottom = B; \
2597 cc_tab[N].clear = C; \
2598 cc_tab[N].valid = true
2600 static void cc_init (void) {
2604 for (i = 0; i < CS_MAX; i++)
2605 cc_tab[i].valid = false;
2606 set_cc (CS_HSTEM, true, 2, true);
2607 set_cc (CS_VSTEM, true, 2, true);
2608 set_cc (CS_VMOVETO, true, 1, true);
2609 set_cc (CS_RLINETO, true, 2, true);
2610 set_cc (CS_HLINETO, true, 1, true);
2611 set_cc (CS_VLINETO, true, 1, true);
2612 set_cc (CS_RRCURVETO, true, 6, true);
2613 set_cc (CS_CLOSEPATH, false, 0, true);
2614 set_cc (CS_CALLSUBR, false, 1, false);
2615 set_cc (CS_RETURN, false, 0, false);
2617 |set_cc(CS_ESCAPE, false, 0, false);|
2619 set_cc (CS_HSBW, true, 2, true);
2620 set_cc (CS_ENDCHAR, false, 0, true);
2621 set_cc (CS_RMOVETO, true, 2, true);
2622 set_cc (CS_HMOVETO, true, 1, true);
2623 set_cc (CS_VHCURVETO, true, 4, true);
2624 set_cc (CS_HVCURVETO, true, 4, true);
2625 set_cc (CS_DOTSECTION, false, 0, true);
2626 set_cc (CS_VSTEM3, true, 6, true);
2627 set_cc (CS_HSTEM3, true, 6, true);
2628 set_cc (CS_SEAC, true, 5, true);
2629 set_cc (CS_SBW, true, 4, true);
2630 set_cc (CS_DIV, false, 2, false);
2631 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2632 set_cc (CS_POP, false, 0, false);
2633 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2639 @d cs_getchar(mp) cdecrypt(*data++, &cr)
2641 @d mark_subr(mp,n) cs_mark(mp,0, n)
2642 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2643 @d SMALL_BUF_SIZE 256
2646 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2647 char buf[SMALL_BUF_SIZE];
2650 va_start (args, fmt);
2651 @= /*@@-bufferoverflowhigh@@*/ @>
2652 (void)vsprintf (buf, fmt, args);
2653 @= /*@@=bufferoverflowhigh@@*/ @>
2655 if (cs_name == NULL) {
2656 mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2658 mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2663 static void cs_mark (MP mp, const char *cs_name, int subr)
2669 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2673 if (cs_name == NULL) {
2675 ptr = mp->ps->subr_tab + subr;
2679 if (mp->ps->cs_notdef != NULL &&
2680 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2681 ptr = mp->ps->cs_notdef;
2683 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2684 if (strcmp (ptr->glyph_name, cs_name) == 0)
2686 if (ptr == mp->ps->cs_ptr) {
2688 mp_snprintf (s,128,"glyph `%s' undefined", cs_name);
2692 if (ptr->glyph_name == notdef)
2693 mp->ps->cs_notdef = ptr;
2696 /* only marked CharString entries and invalid entries can be skipped;
2697 valid marked subrs must be parsed to keep the stack in sync */
2698 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2700 ptr->is_used = true;
2702 cs_len = (int)ptr->cslen;
2703 data = ptr->data + 4;
2704 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2705 (void)cs_getchar (mp);
2706 while (cs_len > 0) {
2708 b = cs_getchar (mp);
2712 else if (b <= 250) {
2714 a = (int)((unsigned)(b - 247) << 8) + 108 + cs_getchar (mp);
2715 } else if (b <= 254) {
2717 a = -(int)((unsigned)(b - 251) << 8) - 108 - cs_getchar (mp);
2720 a = (cs_getchar (mp) & 0xff) << 24;
2721 a |= (cs_getchar (mp) & 0xff) << 16;
2722 a |= (cs_getchar (mp) & 0xff) << 8;
2723 a |= (cs_getchar (mp) & 0xff) << 0;
2724 if (sizeof (integer) > 4 && (a & 0x80000000))
2729 if (b == CS_ESCAPE) {
2730 b = cs_getchar (mp) + CS_1BYTE_MAX;
2734 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2740 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2744 if (stack_ptr - cc_stack < cc->nargs)
2745 cs_warn (mp,cs_name, subr,
2746 "less arguments on stack (%i) than required (%i)",
2747 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2748 else if (stack_ptr - cc_stack > cc->nargs)
2749 cs_warn (mp,cs_name, subr,
2750 "more arguments on stack (%i) than required (%i)",
2751 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2753 switch (cc - cc_tab) {
2758 if (!mp->ps->subr_tab[a1].valid) {
2759 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2767 case CS_CALLOTHERSUBR:
2768 if (cc_get (-1) == 3)
2769 lastargOtherSubr3 = cc_get (-3);
2770 a1 = cc_get (-2) + 2;
2774 cc_push (lastargOtherSubr3);
2775 /* the only case when we care about the value being pushed onto
2776 stack is when POP follows CALLOTHERSUBR (changing hints by
2784 mark_cs (mp,standard_glyph_names[a1]);
2785 mark_cs (mp,standard_glyph_names[a2]);
2794 cs_error: /* an error occured during parsing */
2797 ptr->is_used = false;
2800 static void t1_subset_ascii_part (MP mp, font_number tex_font, fm_entry *fm_cur)
2804 while (!t1_prefix ("/Encoding")) {
2805 t1_scan_param (mp,tex_font, fm_cur);
2809 t1_builtin_enc (mp);
2810 if (is_reencoded (fm_cur))
2811 mp->ps->t1_glyph_names = external_enc ();
2813 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2815 |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2816 make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2817 update_subset_tag ();
2820 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2821 t1_puts (mp,"/Encoding StandardEncoding def\n");
2824 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2825 for (i = 0, j = 0; i < 256; i++) {
2826 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2828 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
2829 "dup %i /%s put\n", (int) t1_char (i),
2830 mp->ps->t1_glyph_names[i]);
2831 t1_puts(mp,mp->ps->t1_line_array);
2834 /* We didn't mark anything for the Encoding array. */
2835 /* We add "dup 0 /.notdef put" for compatibility */
2836 /* with Acrobat 5.0. */
2838 t1_puts (mp,"dup 0 /.notdef put\n");
2839 t1_puts (mp,"readonly def\n");
2843 t1_scan_param (mp,tex_font, fm_cur);
2844 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
2847 while (mp->ps->t1_in_eexec == 0);
2850 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
2851 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
2853 static void cs_init (MP mp) {
2854 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2855 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2856 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2857 mp->ps->cs_token_pair = NULL;
2858 mp->ps->subr_tab = NULL;
2859 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2860 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2863 static void init_cs_entry ( cs_entry * cs) {
2865 cs->glyph_name = NULL;
2868 cs->is_used = false;
2872 static void t1_mark_glyphs (MP mp, font_number tex_font);
2874 static void t1_read_subrs (MP mp, font_number tex_font, fm_entry *fm_cur)
2879 while (!(t1_charstrings () || t1_subrs ())) {
2880 t1_scan_param (mp,tex_font, fm_cur);
2885 mp->ps->t1_cs = true;
2886 mp->ps->t1_scan = false;
2889 mp->ps->subr_size_pos = (int)(strlen ("/Subrs") + 1);
2890 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2891 mp->ps->subr_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2892 if (mp->ps->subr_size == 0) {
2893 while (!t1_charstrings ())
2897 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
2898 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,(size_t)mp->ps->subr_size, sizeof (cs_entry));
2899 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2900 init_cs_entry (ptr);
2901 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2903 while (mp->ps->t1_cslen) {
2907 /* mark the first four entries without parsing */
2908 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2909 mp->ps->subr_tab[i].is_used = true;
2910 /* the end of the Subrs array might have more than one line so we need to
2911 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2912 the Subrs array followed by the CharStrings dict immediately (synthetic
2913 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2914 we will treat the font as synthetic and ignore everything until next
2917 #define POST_SUBRS_SCAN 5
2919 *mp->ps->t1_buf_array = 0;
2920 for (i = 0; i < POST_SUBRS_SCAN; i++) {
2921 if (t1_charstrings ())
2923 s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2924 alloc_array (t1_buf, s, T1_BUF_SIZE);
2925 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2928 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2929 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2930 suppose synthetic font */
2931 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2933 mp_xfree (ptr->data);
2934 mp_xfree (mp->ps->subr_tab);
2935 mp_xfree (mp->ps->subr_array_start);
2936 mp_xfree (mp->ps->subr_array_end);
2938 mp->ps->t1_cs = false;
2939 mp->ps->t1_synthetic = true;
2940 while (!(t1_charstrings () || t1_subrs ()))
2947 static void t1_flush_cs (MP mp, boolean is_subr)
2950 byte *r, *return_cs = NULL;
2951 cs_entry *tab, *end_tab, *ptr;
2952 char *start_line, *line_end;
2953 int count, size_pos;
2954 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2956 start_line = mp->ps->subr_array_start;
2957 line_end = mp->ps->subr_array_end;
2958 size_pos = mp->ps->subr_size_pos;
2959 tab = mp->ps->subr_tab;
2960 count = mp->ps->subr_max + 1;
2961 end_tab = mp->ps->subr_tab + count;
2963 start_line = mp->ps->cs_dict_start;
2964 line_end = mp->ps->cs_dict_end;
2965 size_pos = mp->ps->cs_size_pos;
2966 tab = mp->ps->cs_tab;
2967 end_tab = mp->ps->cs_ptr;
2968 count = mp->ps->cs_count;
2970 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2971 for (p = start_line; p - start_line < size_pos;)
2972 *mp->ps->t1_line_ptr++ = *p++;
2973 while (mp_isdigit (*p))
2975 mp_snprintf (mp->ps->t1_line_ptr, (int)mp->ps->t1_line_limit, "%u", (unsigned)count);
2976 strcat (mp->ps->t1_line_ptr, p);
2977 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2980 /* create |return_cs| to replace unsused subr's */
2984 return_cs = mp_xmalloc (mp, (size_t)(mp->ps->t1_lenIV + 1) , sizeof(byte));
2985 if ( mp->ps->t1_lenIV > 0) {
2986 for (cs_len = 0, r = return_cs;
2987 cs_len<(unsigned short)mp->ps->t1_lenIV; cs_len++, r++)
2988 *r = cencrypt (0x00, &cr);
2989 *r = cencrypt (CS_RETURN, &cr);
2991 *return_cs = CS_RETURN;
2996 for (ptr = tab; ptr < end_tab; ptr++) {
2999 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3000 "dup %i %u", (int) (ptr - tab), ptr->cslen);
3002 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3003 "/%s %u", ptr->glyph_name, ptr->cslen);
3004 p = strend (mp->ps->t1_line_array);
3005 memcpy (p, ptr->data, (size_t)ptr->len);
3006 mp->ps->t1_line_ptr = p + ptr->len;
3009 /* replace unsused subr's by |return_cs| */
3011 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3012 "dup %i %u%s ", (int) (ptr - tab),
3013 cs_len, mp->ps->cs_token_pair[0]);
3014 p = strend (mp->ps->t1_line_array);
3015 memcpy (p, return_cs, (size_t)cs_len);
3016 mp->ps->t1_line_ptr = p + cs_len;
3018 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3019 " %s", mp->ps->cs_token_pair[1]);
3020 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3024 mp_xfree (ptr->data);
3025 if (ptr->glyph_name != notdef)
3026 mp_xfree (ptr->glyph_name);
3028 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "%s", line_end);
3029 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3032 mp_xfree (return_cs);
3034 mp_xfree (start_line);
3035 mp_xfree (line_end);
3038 static void t1_mark_glyphs (MP mp, font_number tex_font)
3041 char *charset = extra_charset ();
3044 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
3045 if (mp->ps->cs_tab != NULL)
3046 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3048 ptr->is_used = true;
3049 if (mp->ps->subr_tab != NULL) {
3050 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3052 ptr->is_used = true;
3053 mp->ps->subr_max = mp->ps->subr_size - 1;
3057 mark_cs (mp,notdef);
3058 for (i = 0; i < 256; i++)
3059 if (is_used_char (i)) {
3060 if (mp->ps->t1_glyph_names[i] == notdef) {
3062 mp_snprintf(S,128, "character %i is mapped to %s", i, notdef);
3065 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3067 if (charset == NULL)
3069 g = s = charset + 1; /* skip the first '/' */
3072 while (*s != '/' && s < r)
3074 *s = 0; /* terminate g by rewriting '/' to 0 */
3079 if (mp->ps->subr_tab != NULL)
3080 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3081 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3083 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3084 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3087 static void t1_subset_charstrings (MP mp, font_number tex_font)
3090 mp->ps->cs_size_pos =
3091 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3092 - mp->ps->t1_line_array + 1;
3093 /* |cs_size_pos| points to the number indicating
3094 dict size after "/CharStrings" */
3095 mp->ps->cs_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3096 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,(size_t)mp->ps->cs_size, sizeof(cs_entry));
3097 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3098 init_cs_entry (ptr);
3099 mp->ps->cs_notdef = NULL;
3100 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3102 while (mp->ps->t1_cslen) {
3106 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3107 t1_mark_glyphs (mp,tex_font);
3108 if (mp->ps->subr_tab != NULL) {
3109 if (mp->ps->cs_token_pair == NULL)
3111 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3114 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3120 static void t1_subset_end (MP mp)
3122 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3123 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3127 while (!t1_end_eexec ())
3128 t1_getline (mp); /* ignore the rest */
3129 t1_putline (mp); /* write "mark currentfile closefile" */
3131 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3136 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3137 while (!t1_cleartomark ()) {
3141 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3142 t1_check_end (mp); /* write "{restore}if" if found */
3146 static int t1_updatefm (MP mp, font_number f, fm_entry *fm)
3149 mp->ps->read_encoding_only = true;
3150 if (!t1_open_fontfile (mp,fm,NULL)) {
3153 t1_scan_only (mp,f, fm);
3154 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3156 while (*p != ' ' && *p != 0)
3160 t1_close_font_file (mp,"");
3165 static void writet1 (MP mp, font_number tex_font, fm_entry *fm_cur) {
3166 unsigned save_selector = mp->selector;
3167 mp_normalize_selector(mp);
3168 mp->ps->read_encoding_only = false;
3169 if (!is_included (fm_cur)) { /* scan parameters from font file */
3170 if (!t1_open_fontfile (mp,fm_cur,"{"))
3172 t1_scan_only (mp,tex_font, fm_cur);
3173 t1_close_font_file (mp,"}");
3176 if (!is_subsetted (fm_cur)) { /* include entire font */
3177 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3179 t1_include (mp,tex_font,fm_cur);
3180 t1_close_font_file (mp,">>");
3183 /* partial downloading */
3184 if (!t1_open_fontfile (mp,fm_cur,"<"))
3186 t1_subset_ascii_part (mp,tex_font,fm_cur);
3187 t1_start_eexec (mp,fm_cur);
3190 t1_read_subrs (mp,tex_font, fm_cur);
3191 t1_subset_charstrings (mp,tex_font);
3193 t1_close_font_file (mp,">");
3194 mp->selector = save_selector;
3198 static void t1_free (MP mp);
3201 static void t1_free (MP mp) {
3202 mp_xfree (mp->ps->t1_line_array);
3203 mp_xfree (mp->ps->t1_buf_array);
3207 @* \[44d] Embedding fonts.
3209 @ The |tfm_num| is officially of type |font_number|, but that
3210 type does not exist yet at this point in the output order.
3214 char *tfm_name; /* TFM file name */
3215 char *ps_name; /* PostScript name */
3216 integer flags; /* font flags */
3217 char *ff_name; /* font file name */
3218 char *subset_tag; /* pseudoUniqueTag for subsetted font */
3219 enc_entry *encoding; /* pointer to corresponding encoding */
3220 unsigned int tfm_num; /* number of the TFM refering this entry */
3221 unsigned short type; /* font type (T1/TTF/...) */
3222 short slant; /* SlantFont */
3223 short extend; /* ExtendFont */
3224 integer ff_objnum; /* FontFile object number */
3225 integer fn_objnum; /* FontName/BaseName object number */
3226 integer fd_objnum; /* FontDescriptor object number */
3227 char *charset; /* string containing used glyphs */
3228 boolean all_glyphs; /* embed all glyphs? */
3229 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
3230 short tfm_avail; /* flags whether a tfm is available */
3231 short pid; /* Pid for truetype fonts */
3232 short eid; /* Eid for truetype fonts */
3238 #define FONTNAME_BUF_SIZE 128
3239 boolean fontfile_found;
3240 boolean is_otf_font;
3241 char fontname_buf[FONTNAME_BUF_SIZE];
3249 @d set_included(fm) ((fm)->type |= F_INCLUDED)
3250 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
3251 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
3252 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
3254 @d is_included(fm) ((fm)->type & F_INCLUDED)
3255 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
3256 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
3257 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
3258 @d is_reencoded(fm) ((fm)->encoding != NULL)
3259 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
3260 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
3262 @d fm_slant(fm) (fm)->slant
3263 @d fm_extend(fm) (fm)->extend
3264 @d fm_fontfile(fm) (fm)->ff_name
3267 static boolean mp_font_is_reencoded (MP mp, font_number f);
3268 static boolean mp_font_is_included (MP mp, font_number f);
3269 static boolean mp_font_is_subsetted (MP mp, font_number f);
3272 boolean mp_font_is_reencoded (MP mp, font_number f) {
3274 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3276 && (fm->ps_name != NULL)
3277 && is_reencoded (fm))
3282 boolean mp_font_is_included (MP mp, font_number f) {
3284 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3286 && (fm->ps_name != NULL && fm->ff_name != NULL)
3287 && is_included (fm))
3292 boolean mp_font_is_subsetted (MP mp, font_number f) {
3294 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
3296 && (fm->ps_name != NULL && fm->ff_name != NULL)
3297 && is_included (fm) && is_subsetted (fm))
3304 static char * mp_fm_encoding_name (MP mp, font_number f);
3305 static char * mp_fm_font_name (MP mp, font_number f);
3306 static char * mp_fm_font_subset_name (MP mp, font_number f);
3309 @c char * mp_fm_encoding_name (MP mp, font_number f) {
3312 if (mp_has_fm_entry (mp, f, &fm)) {
3313 if (fm != NULL && (fm->ps_name != NULL)) {
3314 if (is_reencoded (fm)) {
3316 if (e->enc_name!=NULL)
3317 return mp_xstrdup(mp,e->enc_name);
3323 print_err ("fontmap encoding problems for font ");
3324 mp_print(mp,mp->font_name[f]);
3328 char * mp_fm_font_name (MP mp, font_number f) {
3330 if (mp_has_fm_entry (mp, f,&fm)) {
3331 if (fm != NULL && (fm->ps_name != NULL)) {
3332 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3333 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3334 if (t1_updatefm(mp,f,fm)) {
3335 mp->font_ps_name_fixed[f] = true;
3337 print_err ("font loading problems for font ");
3338 mp_print(mp,mp->font_name[f]);
3342 return mp_xstrdup(mp,fm->ps_name);
3345 print_err ("fontmap name problems for font ");
3346 mp_print(mp,mp->font_name[f]);
3351 static char * mp_fm_font_subset_name (MP mp, font_number f) {
3353 if (mp_has_fm_entry (mp, f, &fm)) {
3354 if (fm != NULL && (fm->ps_name != NULL)) {
3355 if (is_subsetted(fm)) {
3356 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3357 mp_snprintf(s,(int)strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3360 return mp_xstrdup(mp,fm->ps_name);
3364 print_err ("fontmap name problems for font ");
3365 mp_print(mp,mp->font_name[f]);
3371 static integer mp_fm_font_slant (MP mp, font_number f);
3372 static integer mp_fm_font_extend (MP mp, font_number f);
3375 @c static integer mp_fm_font_slant (MP mp, font_number f) {
3377 if (mp_has_fm_entry (mp, f, &fm)) {
3378 if (fm != NULL && (fm->ps_name != NULL)) {
3384 static integer mp_fm_font_extend (MP mp, font_number f) {
3386 if (mp_has_fm_entry (mp, f, &fm)) {
3387 if (fm != NULL && (fm->ps_name != NULL)) {
3395 static boolean mp_do_ps_font (MP mp, font_number f);
3397 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3399 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3402 if (is_truetype(fm_cur) ||
3403 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3406 if (is_included(fm_cur)) {
3407 mp_ps_print_nl(mp,"%%BeginResource: font ");
3408 if (is_subsetted(fm_cur)) {
3409 mp_ps_print(mp, fm_cur->subset_tag);
3410 mp_ps_print_char(mp,'-');
3412 mp_ps_print(mp, fm_cur->ps_name);
3414 writet1 (mp,f,fm_cur);
3415 mp_ps_print_nl(mp,"%%EndResource");
3421 @ Included subset fonts do not need and encoding vector, make
3422 sure we skip that case.
3425 static void mp_list_used_resources (MP mp, int prologues, int procset);
3427 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
3428 font_number f; /* fonts used in a text node or as loop counters */
3429 int ff; /* a loop counter */
3430 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3433 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
3435 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3438 for (f=null_font+1;f<=mp->last_fnum;f++) {
3439 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3440 for (ff=ldf;ff>=null_font;ff--) {
3441 if ( mp_has_font_size(mp,(font_number)ff) )
3442 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3445 if ( mp_font_is_subsetted(mp,f) )
3447 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
3448 (size_t)mp->max_print_line )
3449 mp_ps_print_nl(mp, "%%+ encoding");
3452 mp_ps_print_nl(mp, "%%+ encoding");
3454 mp_ps_print_char(mp, ' ');
3455 mp_ps_print(mp, mp->font_enc_name[f]);
3463 for (f=null_font+1;f<=mp->last_fnum;f++) {
3464 if ( mp_has_font_size(mp,f) ) {
3465 for (ff=ldf;ff>=null_font;ff--) {
3466 if ( mp_has_font_size(mp,(font_number)ff) )
3467 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3470 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
3471 (size_t)mp->max_print_line )
3472 mp_ps_print_nl(mp, "%%+ font");
3475 mp_ps_print_nl(mp, "%%+ font");
3477 mp_ps_print_char(mp, ' ');
3478 if ( (prologues==3)&&
3479 (mp_font_is_subsetted(mp,f)) )
3480 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3482 mp_ps_print(mp, mp->font_ps_name[f]);
3492 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
3494 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
3495 font_number f; /* fonts used in a text node or as loop counters */
3496 int ff; /* a loop counter */
3497 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3500 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3502 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3505 for (f=null_font+1;f<=mp->last_fnum;f++) {
3506 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3507 for (ff=ldf;ff>= null_font;ff++) {
3508 if ( mp_has_font_size(mp,(font_number)ff) )
3509 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3512 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3514 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(size_t)mp->max_print_line )
3515 mp_ps_print_nl(mp, "%%+ encoding");
3518 mp_ps_print_nl(mp, "%%+ encoding");
3520 mp_ps_print_char(mp, ' ');
3521 mp_ps_print(mp, mp->font_enc_name[f]);
3530 for (f=null_font+1;f<=mp->last_fnum;f++) {
3531 if ( mp_has_font_size(mp,f) ) {
3532 for (ff=ldf;ff>= null_font;ff--) {
3533 if ( mp_has_font_size(mp,(font_number)ff) )
3534 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3537 if ( ! mp_font_is_included(mp,f) )
3539 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
3540 mp_ps_print_nl(mp, "%%+ font");
3543 mp_ps_print_nl(mp, "%%+ font");
3545 mp_ps_print_char(mp, ' ');
3546 if ( mp_font_is_subsetted(mp,f) )
3547 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3549 mp_ps_print(mp, mp->font_ps_name[f]);
3559 @ @<Declarations...@>=
3560 static void mp_list_needed_resources (MP mp, int prologues);
3562 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
3563 font_number f; /* fonts used in a text node or as loop counters */
3564 int ff; /* a loop counter */
3565 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3569 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3570 if ( mp_has_font_size(mp,f)) {
3571 for (ff=ldf;ff>=null_font;ff--) {
3572 if ( mp_has_font_size(mp,(font_number)ff) )
3573 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3576 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3578 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
3579 mp_ps_print_nl(mp, "%%+ font");
3582 mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
3584 mp_ps_print_char(mp, ' ');
3585 mp_ps_print(mp, mp->font_ps_name[f]);
3591 if ( ! firstitem ) {
3595 for (f=null_font+1;f<= mp->last_fnum;f++) {
3596 if ( mp_has_font_size(mp,f) ) {
3597 for (ff=ldf;ff>=null_font;ff-- ) {
3598 if ( mp_has_font_size(mp,(font_number)ff) )
3599 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3602 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3604 mp_ps_print(mp, "%%IncludeResource: font ");
3605 mp_ps_print(mp, mp->font_ps_name[f]);
3616 static void mp_write_font_definition (MP mp, font_number f, int prologues);
3620 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3621 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3623 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
3624 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3625 (mp_fm_font_extend(mp,f)!=0)||
3626 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3627 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3628 if ( (mp_font_is_subsetted(mp,f))&&
3629 (mp_font_is_included(mp,f))&&(prologues==3))
3630 mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3632 mp_ps_name_out(mp, mp->font_ps_name[f],true);
3633 mp_ps_print(mp, " fcp");
3635 if ( applied_reencoding(f) ) {
3636 mp_ps_print(mp, "/Encoding ");
3637 mp_ps_print(mp, mp->font_enc_name[f]);
3638 mp_ps_print(mp, " def ");
3640 if ( mp_fm_font_slant(mp,f)!=0 ) {
3641 mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
3642 mp_ps_print(mp, " SlantFont ");
3644 if ( mp_fm_font_extend(mp,f)!=0 ) {
3645 mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3646 mp_ps_print(mp, " ExtendFont ");
3648 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3649 mp_ps_print(mp, " 890 ScaleFont ");
3650 mp_ps_print(mp, " 277 SlantFont ");
3652 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3653 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3654 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3655 mp_ps_print(mp, "/space[0 -278]def ");
3656 mp_ps_print(mp, "/a12[-904 -939]def ");
3657 mp_ps_print(mp, "end def ");
3659 mp_ps_print(mp, "currentdict end");
3661 mp_ps_print_defined_name(mp,f,prologues);
3662 mp_ps_print(mp, " exch definefont pop");
3668 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
3671 @c static void mp_ps_print_defined_name(MP mp, font_number f, int prologues) {
3672 mp_ps_print(mp, " /");
3673 if ((mp_font_is_subsetted(mp,f))&&
3674 (mp_font_is_included(mp,f))&&(prologues==3))
3675 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3677 mp_ps_print(mp, mp->font_ps_name[f]);
3678 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 )
3679 mp_ps_print(mp, "-Slanted");
3680 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 )
3681 mp_ps_print(mp, "-Reverse");
3682 if ( applied_reencoding(f) ) {
3683 mp_ps_print(mp, "-");
3684 mp_ps_print(mp, mp->font_enc_name[f]);
3686 if ( mp_fm_font_slant(mp,f)!=0 ) {
3687 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,f)) ;
3689 if ( mp_fm_font_extend(mp,f)!=0 ) {
3690 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3694 @ @<Include encodings and fonts for edge structure~|h|@>=
3695 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
3696 @<Embed fonts that are available@>
3698 @ @<Embed fonts that are available@>=
3701 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3704 for (f=null_font+1;f<=mp->last_fnum;f++) {
3705 if ( cur_fsize[f]!=null ) {
3706 if (prologues==3 ) {
3707 if ( ! mp_do_ps_font(mp,f) ) {
3708 if ( mp_has_fm_entry(mp,f, NULL) ) {
3709 print_err("Font embedding failed");
3714 cur_fsize[f]=link(cur_fsize[f]);
3715 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3719 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3721 } while (! done_fonts);
3724 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3727 mp_apply_mark_string_chars(mp, h, next_size);
3730 @ We also need to keep track of which characters are used in text nodes
3731 in the edge structure that is being shipped out. This is done by procedures
3732 that use the left-over |b3| field in the |char_info| words; i.e.,
3733 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3736 enum mp_char_mark_state {mp_unused=0, mp_used};
3739 static void mp_mark_string_chars (MP mp,font_number f, char *s) ;
3742 void mp_mark_string_chars (MP mp,font_number f, char *s) {
3743 integer b; /* |char_base[f]| */
3744 ASCII_code bc,ec; /* only characters between these bounds are marked */
3745 char *k; /* an index into string |s| */
3751 if ( (*k>=(char)bc)&&(*k<=(char)ec) )
3752 mp->font_info[b+*k].qqqq.b3=mp_used;
3759 static void mp_unmark_font (MP mp,font_number f) ;
3762 void mp_unmark_font (MP mp,font_number f) {
3763 int k; /* an index into |font_info| */
3764 for (k= mp->char_base[f]+mp->font_bc[f];
3765 k<=mp->char_base[f]+mp->font_ec[f];
3767 mp->font_info[k].qqqq.b3=mp_unused;
3772 static void mp_print_improved_prologue (MP mp, mp_edge_object *h, int p1, int procset) ;
3775 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
3776 quarterword next_size; /* the size index for fonts being listed */
3777 pointer *cur_fsize; /* current positions in |font_sizes| */
3778 boolean done_fonts; /* have we finished listing the fonts in the header? */
3779 font_number f; /* a font number for loops */
3780 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer));
3781 mp_list_used_resources(mp, prologues, procset);
3782 mp_list_supplied_resources(mp, prologues, procset);
3783 mp_list_needed_resources(mp, prologues);
3784 mp_ps_print_nl(mp, "%%EndComments");
3785 mp_ps_print_nl(mp, "%%BeginProlog");
3787 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
3789 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3790 mp_ps_print_nl(mp, "/bd{bind def}bind def"
3791 "/fshow {exch findfont exch scalefont setfont show}bd");
3792 if ( procset>0 ) @<Print the procset@>;
3793 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
3794 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3795 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3796 "/fmd{/FontMatrix exch def}bd");
3797 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3798 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3799 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3800 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3801 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3802 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3803 mp_ps_print_nl(mp, "%%EndResource");
3804 @<Include encodings and fonts for edge structure~|h|@>;
3805 mp_ps_print_nl(mp, "%%EndProlog");
3806 mp_ps_print_nl(mp, "%%BeginSetup");
3808 for (f=null_font+1;f<=mp->last_fnum;f++) {
3809 if ( mp_has_font_size(mp,f) ) {
3810 if ( mp_has_fm_entry(mp,f,NULL) ) {
3811 mp_write_font_definition(mp,f, prologues);
3812 mp_ps_name_out(mp, mp->font_name[f],true);
3813 mp_ps_print_defined_name(mp,f, prologues);
3814 mp_ps_print(mp, " def");
3817 mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3819 mp_ps_name_out(mp, mp->font_name[f],true);
3820 mp_ps_name_out(mp, mp->font_name[f],true);
3821 mp_ps_print(mp, " def");
3826 mp_ps_print_nl(mp, "%%EndSetup");
3827 mp_ps_print_nl(mp, "%%Page: 1 1");
3829 mp_xfree(cur_fsize);
3833 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues);
3838 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) {
3839 quarterword next_size; /* the size index for fonts being listed */
3840 pointer *cur_fsize; /* current positions in |font_sizes| */
3841 int ff; /* a loop counter */
3842 boolean done_fonts; /* have we finished listing the fonts in the header? */
3843 font_number f; /* a font number for loops */
3844 scaled ds; /* design size and scale factor for a text node */
3845 int ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3846 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer));
3847 if ( prologues>0 ) {
3848 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3849 |font_sizes| and eliminate duplicates@>;
3852 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3853 do { done_fonts=true;
3854 for (f=null_font+1;f<=mp->last_fnum;f++) {
3855 if ( cur_fsize[f]!=null ) {
3856 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3858 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
3860 if ( ! done_fonts ) {
3861 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3864 } while (! done_fonts);
3866 mp_xfree(cur_fsize);
3867 return (font_number)ldf;
3870 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3871 for (f=null_font+1;f<= mp->last_fnum;f++)
3872 cur_fsize[f]=mp->font_sizes[f]
3874 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3875 so we carefully remove duplicates. There is no harm in using a slow, brute-force
3878 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3881 for (f=null_font+1;f<= mp->last_fnum;f++) {
3882 if ( mp->font_sizes[f]!=null ) {
3883 if ( ldf==null_font )
3884 mp_ps_print_nl(mp, "%%DocumentFonts:");
3885 for (ff=ldf;ff>=null_font;ff--) {
3886 if ( mp->font_sizes[ff]!=null )
3887 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3890 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
3891 mp_ps_print_nl(mp, "%%+");
3892 mp_ps_print_char(mp, ' ');
3893 mp_ps_print(mp, mp->font_ps_name[f]);
3902 static void mp_hex_digit_out (MP mp,quarterword d) {
3903 if ( d<10 ) mp_ps_print_char(mp, d+'0');
3904 else mp_ps_print_char(mp, d+'a'-10);
3907 @ We output the marks as a hexadecimal bit string starting at |c| or
3908 |font_bc[f]|, whichever is greater. If the output has to be truncated
3909 to avoid exceeding |emergency_line_length| the return value says where to
3910 start scanning next time.
3913 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3916 @d emergency_line_length 255
3917 /* \ps\ output lines can be this long in unusual circumstances */
3920 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3921 eight_bits bc,ec; /* only encode characters between these bounds */
3922 integer lim; /* the maximum number of marks to encode before truncating */
3923 int p; /* |font_info| index for the current character */
3924 int d; /* used to construct a hexadecimal digit */
3925 unsigned b; /* used to construct a hexadecimal digit */
3926 lim=4*(emergency_line_length-mp->ps->ps_offset-4);
3930 @<Restrict the range |bc..ec| so that it contains no unused characters
3931 at either end and has length at most |lim|@>;
3932 @<Print the initial label indicating that the bitmap starts at |bc|@>;
3933 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3934 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3940 @ We could save time by setting the return value before the loop that
3941 decrements |ec|, but there is no point in being so tricky.
3943 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3944 p=mp->char_base[f]+bc;
3945 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3948 if ( ec>=(eight_bits)(bc+lim)) ec=(eight_bits)(bc+lim-1);
3949 p=mp->char_base[f]+ec;
3950 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3954 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3955 mp_ps_print_char(mp, ' ');
3956 mp_hex_digit_out(mp, (quarterword)bc / 16);
3957 mp_hex_digit_out(mp, (quarterword)bc % 16);
3958 mp_ps_print_char(mp, ':')
3962 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3964 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3966 mp_hex_digit_out(mp, (quarterword)d);
3969 if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
3972 mp_hex_digit_out(mp, (quarterword)d)
3975 @ Here is a simple function that determines whether there are any marked
3976 characters in font~|f| with character code at least~|c|.
3979 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
3982 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
3983 int p; /* |font_info| index for the current character */
3984 for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
3985 if ( mp->font_info[p].qqqq.b3==mp_used )
3992 @ If the file name is so long that it can't be printed without exceeding
3993 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
3994 line. We might have to repeat line in order to get the character usage
3995 information to fit within |emergency_line_length|.
3997 TODO: these two defines are also defined in mp.w!
3999 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
4000 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
4002 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
4004 while ( mp_check_ps_marks(mp, f,t) ) {
4005 mp_ps_print_nl(mp, "%*Font: ");
4006 if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
4008 mp_ps_print(mp, mp->font_name[f]);
4009 mp_ps_print_char(mp, ' ');
4010 ds=(mp->font_dsize[f] + 8) / 16;
4011 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
4012 if ( mp->ps->ps_offset+12>emergency_line_length ) break;
4013 mp_ps_print_char(mp, ' ');
4014 mp_ps_print_scaled(mp, ds);
4015 if ( mp->ps->ps_offset+5>emergency_line_length ) break;
4016 t=mp_ps_marks_out(mp, f, (eight_bits)t);
4018 cur_fsize[f]=link(cur_fsize[f]);
4021 @ @<Print the procset@>=
4023 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
4024 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
4025 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
4026 "/p{closepath}bd/n{newpath}bd");
4027 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
4028 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
4029 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
4030 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
4031 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4035 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4036 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
4037 might not have reasonable |font_ps_name| entries, but we just charge ahead
4038 anyway. The user should not make \&{prologues} positive if this will cause
4040 @:prologues_}{\&{prologues} primitive@>
4043 static void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset);
4046 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
4049 ldf = mp_print_font_comments (mp, h, prologues);
4051 if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
4052 mp_read_psname_table(mp);
4053 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4054 if ( (prologues>0)||(procset>0) ) {
4055 if ( ldf!=null_font ) {
4056 if ( prologues>0 ) {
4057 for (f=null_font+1;f<=mp->last_fnum;f++) {
4058 if ( mp_has_font_size(mp,f) ) {
4059 mp_ps_name_out(mp, mp->font_name[f],true);
4060 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4061 mp_ps_print(mp, " def");
4066 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4072 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4073 if ( (prologues>0)&&(ldf!=null_font) )
4075 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4077 mp_ps_print_nl(mp, "/bd{bind def}bind def");
4078 @<Print the procset@>;
4079 mp_ps_print_nl(mp, "%%EndResource");
4083 mp_ps_print(mp, "%%EndProlog");
4084 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4087 @ \MP\ used to have one single routine to print to both `write' files
4088 and the PostScript output. Web2c redefines ``Character |k| cannot be
4089 printed'', and that resulted in some bugs where 8-bit characters were
4090 written to the PostScript file (reported by Wlodek Bzyl).
4092 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4093 a plain space, since that makes it easier to parse the result file
4096 @<Character |k| is not allowed in PostScript output@>=
4099 @ We often need to print a pair of coordinates.
4102 void mp_ps_pair_out (MP mp,scaled x, scaled y) {
4104 mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
4105 mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
4109 static void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4112 void mp_ps_print_cmd (MP mp, const char *l, const char *s) {
4113 if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4114 else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4118 static void mp_ps_print_cmd (MP mp, const char *l, const char *s) ;
4121 void mp_ps_string_out (MP mp, const char *s) {
4122 ASCII_code k; /* bits to be converted to octal */
4123 mp_ps_print(mp, "(");
4124 while ((k=(ASCII_code)*s++)) {
4125 if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4126 mp_ps_print_char(mp, '\\');
4129 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4130 mp_ps_print_char(mp, '\\');
4131 mp_ps_print_char(mp, '0'+(k / 64));
4132 mp_ps_print_char(mp, '0'+((k / 8) % 8));
4133 mp_ps_print_char(mp, '0'+(k % 8));
4135 if ( (k=='(')||(k==')')||(k=='\\') )
4136 mp_ps_print_char(mp, '\\');
4137 mp_ps_print_char(mp, k);
4140 mp_ps_print_char(mp, ')');
4144 static void mp_ps_string_out (MP mp, const char *s) ;
4146 @ This is a define because the function does not use its |mp| argument.
4148 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4151 static boolean mp_do_is_ps_name (char *s) {
4152 ASCII_code k; /* the character being checked */
4153 while ((k=(ASCII_code)*s++)) {
4154 if ( (k<=' ')||(k>'~') ) return false;
4155 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4156 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4162 static void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4165 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4166 ps_room(strlen(s)+2);
4167 mp_ps_print_char(mp, ' ');
4168 if ( mp_is_ps_name(mp, s) ) {
4169 if ( lit ) mp_ps_print_char(mp, '/');
4172 mp_ps_string_out(mp, s);
4173 if ( ! lit ) mp_ps_print(mp, "cvx ");
4174 mp_ps_print(mp, "cvn");
4179 @ These special comments described in the {\sl PostScript Language Reference
4180 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4181 We can't normally output ``conforming'' \ps\ because
4182 the structuring conventions don't allow us to say ``Please make sure the
4183 following characters are downloaded and define the \.{fshow} macro to access
4186 The exact bounding box is written out if |mp_prologues<0|, although this
4187 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4188 accurately. (Overfull boxes are avoided if an illustration is made to
4189 match a given \.{\char`\\hsize}.)
4192 static void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues);
4195 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) {
4197 mp_ps_print(mp, "%!PS");
4199 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
4200 mp_ps_print_nl(mp, "%%BoundingBox: ");
4201 if ( hh->minx>hh->maxx) {
4202 mp_ps_print(mp, "0 0 0 0");
4203 } else if ( prologues<0 ) {
4204 mp_ps_pair_out(mp, hh->minx,hh->miny);
4205 mp_ps_pair_out(mp, hh->maxx,hh->maxy);
4207 mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->minx),mp_floor_scaled(mp, hh->miny));
4208 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->maxx),-mp_floor_scaled(mp, -hh->maxy));
4210 mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
4211 if ( hh->minx>hh->maxx ) {
4212 mp_ps_print(mp, "0 0 0 0");
4214 mp_ps_pair_out(mp, hh->minx,hh->miny);
4215 mp_ps_pair_out(mp, hh->maxx,hh->maxy);
4217 mp_ps_print_nl(mp, "%%Creator: MetaPost ");
4218 mp_ps_print(mp, mp_metapost_version());
4219 mp_ps_print_nl(mp, "%%CreationDate: ");
4220 mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
4221 mp_ps_print_char(mp, '.');
4222 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month]));
4223 mp_ps_print_char(mp, '.');
4224 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
4225 mp_ps_print_char(mp, ':');
4226 t=mp_round_unscaled(mp, mp->internal[mp_time]);
4227 mp_ps_print_dd(mp, t / 60);
4228 mp_ps_print_dd(mp, t % 60);
4229 mp_ps_print_nl(mp, "%%Pages: 1");
4232 @ The most important output procedure is the one that gives the \ps\ version of
4236 typedef struct mp_knot {
4237 unsigned short left_type;
4238 unsigned short right_type;
4245 struct mp_knot * next;
4246 unsigned char originator;
4250 #define gr_left_type(A) (A)->left_type
4251 #define gr_right_type(A) (A)->right_type
4252 #define gr_x_coord(A) (A)->x_coord
4253 #define gr_y_coord(A) (A)->y_coord
4254 #define gr_left_x(A) (A)->left_x
4255 #define gr_left_y(A) (A)->left_y
4256 #define gr_right_x(A) (A)->right_x
4257 #define gr_right_y(A) (A)->right_y
4258 #define gr_next_knot(A) (A)->next
4259 #define gr_originator(A) (A)->originator
4261 @ If we want to duplicate a knot node, we can say |copy_knot|:
4264 static mp_knot *mp_gr_copy_knot (MP mp, mp_knot *p) {
4265 mp_knot *q; /* the copy */
4266 q = mp_xmalloc(mp, 1, sizeof (mp_knot));
4267 memcpy(q,p,sizeof (mp_knot));
4268 gr_next_knot(q)=NULL;
4272 @ The |copy_path| routine makes a clone of a given path.
4275 static mp_knot *mp_gr_copy_path (MP mp, mp_knot *p) {
4276 mp_knot *q, *pp, *qq; /* for list manipulation */
4279 q=mp_gr_copy_knot(mp, p);
4283 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4284 qq=gr_next_knot(qq);
4285 pp=gr_next_knot(pp);
4291 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4292 returns a pointer to the first node of the copy, if the path is a cycle,
4293 but to the final node of a non-cyclic copy. The global
4294 variable |path_tail| will point to the final node of the original path;
4295 this trick makes it easier to implement `\&{doublepath}'.
4297 All node types are assumed to be |endpoint| or |explicit| only.
4299 This function is currenly unused.
4302 mp_knot * mp_gr_htap_ypoc (MP mp, mp_knot *p) {
4303 mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4304 q=mp_xmalloc(mp, 1, sizeof (mp_knot)); /* this will correspond to |p| */
4307 gr_right_type(qq)=gr_left_type(pp);
4308 gr_left_type(qq)=gr_right_type(pp);
4309 gr_x_coord(qq)=gr_x_coord(pp);
4310 gr_y_coord(qq)=gr_y_coord(pp);
4311 gr_right_x(qq)=gr_left_x(pp);
4312 gr_right_y(qq)=gr_left_y(pp);
4313 gr_left_x(qq)=gr_right_x(pp);
4314 gr_left_y(qq)=gr_right_y(pp);
4315 gr_originator(qq)=gr_originator(pp);
4316 if ( gr_next_knot(pp)==p ) {
4318 /* |mp->path_tail=pp;| */ /* ? */
4321 rr=mp_xmalloc(mp, 1, sizeof (mp_knot));
4322 gr_next_knot(rr)=qq;
4324 pp=gr_next_knot(pp);
4328 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4329 calling the following subroutine.
4332 static void mp_do_gr_toss_knot_list (mp_knot *p) ;
4335 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4338 void mp_do_gr_toss_knot_list (mp_knot * p) {
4339 mp_knot *q; /* the node being freed */
4340 mp_knot *r; /* the next node */
4353 static void mp_gr_ps_path_out (MP mp, mp_knot *h) {
4354 mp_knot *p, *q; /* for scanning the path */
4355 scaled d; /* a temporary value */
4356 boolean curved; /* |true| unless the cubic is almost straight */
4358 mp_ps_print_cmd(mp, "newpath ","n ");
4359 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4360 mp_ps_print_cmd(mp, "moveto","m");
4363 if ( gr_right_type(p)==mp_endpoint ) {
4364 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4368 @<Start a new line and print the \ps\ commands for the curve from
4372 mp_ps_print_cmd(mp, " closepath"," p");
4375 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4377 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4380 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4381 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4382 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4383 mp_ps_print_cmd(mp, "curveto","c");
4385 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4386 mp_ps_print_cmd(mp, "lineto","l");
4389 @ Two types of straight lines come up often in \MP\ paths:
4390 cubics with zero initial and final velocity as created by |make_path| or
4391 |make_envelope|, and cubics with control points uniformly spaced on a line
4392 as created by |make_choices|.
4394 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4396 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4397 if ( gr_right_x(p)==gr_x_coord(p) )
4398 if ( gr_right_y(p)==gr_y_coord(p) )
4399 if ( gr_left_x(q)==gr_x_coord(q) )
4400 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4401 d=gr_left_x(q)-gr_right_x(p);
4402 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4403 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4404 { d=gr_left_y(q)-gr_right_y(p);
4405 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4406 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4409 @ The colored objects use a struct with anonymous fields to express the color parts:
4413 int a_val, b_val, c_val, d_val;
4416 @ The exported form of a dash pattern is simpler than the internal
4417 format, it is closely modelled to the PostScript model. The array of
4418 dashes is ended by a single negative value, because this is not
4419 allowed in PostScript.
4429 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
4432 static void mp_do_gr_toss_dashes(mp_dash_object *dl);
4435 void mp_do_gr_toss_dashes(mp_dash_object *dl) {
4438 mp_xfree(dl->array);
4444 static mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) {
4445 mp_dash_object *q = NULL;
4449 q = mp_xmalloc(mp, 1, sizeof (mp_dash_object));
4450 memcpy (q,dl,sizeof(mp_dash_object));
4451 if (dl->array != NULL) {
4453 while (*(dl->array+i) != -1) i++;
4454 q->array = mp_xmalloc(mp, i, sizeof (scaled));
4455 memcpy(q->array,dl->array, (i*sizeof(scaled)));
4461 @ Now for outputting the actual graphic objects. First, set up some
4462 structures and access macros.
4464 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4467 #define gr_type(A) (A)->type
4468 #define gr_link(A) (A)->next
4469 #define gr_color_model(A) (A)->color_model
4470 #define gr_red_val(A) (A)->color.a_val
4471 #define gr_green_val(A) (A)->color.b_val
4472 #define gr_blue_val(A) (A)->color.c_val
4473 #define gr_cyan_val(A) (A)->color.a_val
4474 #define gr_magenta_val(A) (A)->color.b_val
4475 #define gr_yellow_val(A) (A)->color.c_val
4476 #define gr_black_val(A) (A)->color.d_val
4477 #define gr_grey_val(A) (A)->color.a_val
4478 #define gr_path_p(A) (A)->path_p
4479 #define gr_htap_p(A) ((mp_fill_object *)A)->htap_p
4480 #define gr_pen_p(A) (A)->pen_p
4481 #define gr_ljoin_val(A) (A)->ljoin
4482 #define gr_lcap_val(A) ((mp_stroked_object *)A)->lcap
4483 #define gr_miterlim_val(A) (A)->miterlim
4484 #define gr_pre_script(A) (A)->pre_script
4485 #define gr_post_script(A) (A)->post_script
4486 #define gr_dash_p(A) ((mp_stroked_object *)A)->dash_p
4487 #define gr_size_index(A) ((mp_text_object *)A)->size_index
4488 #define gr_text_p(A) ((mp_text_object *)A)->text_p
4489 #define gr_font_n(A) ((mp_text_object *)A)->font_n
4490 #define gr_font_name(A) ((mp_text_object *)A)->font_name
4491 #define gr_font_dsize(A) ((mp_text_object *)A)->font_dsize
4492 #define gr_width_val(A) ((mp_text_object *)A)->width
4493 #define gr_height_val(A) ((mp_text_object *)A)->height
4494 #define gr_depth_val(A) ((mp_text_object *)A)->depth
4495 #define gr_tx_val(A) ((mp_text_object *)A)->tx
4496 #define gr_ty_val(A) ((mp_text_object *)A)->ty
4497 #define gr_txx_val(A) ((mp_text_object *)A)->txx
4498 #define gr_txy_val(A) ((mp_text_object *)A)->txy
4499 #define gr_tyx_val(A) ((mp_text_object *)A)->tyx
4500 #define gr_tyy_val(A) ((mp_text_object *)A)->tyy
4503 #define GRAPHIC_BODY \
4505 struct mp_graphic_object * next
4507 typedef struct mp_graphic_object {
4509 } mp_graphic_object;
4511 typedef struct mp_text_object {
4516 unsigned char color_model;
4517 unsigned char size_index;
4520 unsigned int font_dsize ;
4521 unsigned int font_n ;
4533 typedef struct mp_fill_object {
4538 unsigned char color_model;
4539 unsigned char ljoin ;
4546 typedef struct mp_stroked_object {
4551 unsigned char color_model;
4552 unsigned char ljoin ;
4553 unsigned char lcap ;
4557 mp_dash_object *dash_p;
4558 } mp_stroked_object;
4560 typedef struct mp_clip_object {
4565 typedef struct mp_bounds_object {
4570 typedef struct mp_special_object {
4573 } mp_special_object ;
4575 typedef struct mp_edge_object {
4576 struct mp_graphic_object * body;
4577 struct mp_edge_object * next;
4580 int minx, miny, maxx, maxy;
4581 int width, height, depth, ital_corr;
4585 @ @<Exported function headers@>=
4586 mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4589 mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4590 mp_graphic_object *p;
4593 case mp_fill_code: size = sizeof(mp_fill_object); break;
4594 case mp_stroked_code: size = sizeof(mp_stroked_object); break;
4595 case mp_text_code: size = sizeof(mp_text_object); break;
4596 case mp_start_clip_code: size = sizeof(mp_clip_object); break;
4597 case mp_start_bounds_code: size = sizeof(mp_bounds_object); break;
4598 case mp_special_code: size = sizeof(mp_special_object); break;
4599 default: size = sizeof(mp_graphic_object); break;
4601 p = (mp_graphic_object *)mp_xmalloc(mp,1,size);
4607 @ We need to keep track of several parameters from the \ps\ graphics state.
4609 This allows us to be sure that \ps\ has the correct values when they are
4610 needed without wasting time and space setting them unnecessarily.
4612 @d gs_red mp->ps->gs_state->red_field
4613 @d gs_green mp->ps->gs_state->green_field
4614 @d gs_blue mp->ps->gs_state->blue_field
4615 @d gs_black mp->ps->gs_state->black_field
4616 @d gs_colormodel mp->ps->gs_state->colormodel_field
4617 @d gs_ljoin mp->ps->gs_state->ljoin_field
4618 @d gs_lcap mp->ps->gs_state->lcap_field
4619 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4620 @d gs_miterlim mp->ps->gs_state->miterlim_field
4621 @d gs_dash_p mp->ps->gs_state->dash_p_field
4622 @d gs_dash_init_done mp->ps->gs_state->dash_done_field
4623 @d gs_previous mp->ps->gs_state->previous_field
4624 @d gs_width mp->ps->gs_state->width_field
4627 typedef struct _gs_state {
4629 scaled green_field ;
4631 scaled black_field ;
4632 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4633 quarterword colormodel_field ;
4634 /* the current colormodel */
4635 quarterword ljoin_field ;
4636 quarterword lcap_field ;
4637 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4638 quarterword adj_wx_field ;
4639 /* what resolution-dependent adjustment applies to the width */
4640 scaled miterlim_field ;
4641 /* the value from the last \&{setmiterlimit} command */
4642 mp_dash_object * dash_p_field ;
4643 /* edge structure for last \&{setdash} command */
4644 boolean dash_done_field ; /* to test for initial \&{setdash} */
4645 struct _gs_state * previous_field ;
4646 /* backlink to the previous |_gs_state| structure */
4647 scaled width_field ;
4648 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4653 struct _gs_state * gs_state;
4656 mp->ps->gs_state=NULL;
4658 @ @<Dealloc variables@>=
4659 mp_xfree(mp->ps->gs_state);
4661 @ To avoid making undue assumptions about the initial graphics state, these
4662 parameters are given special values that are guaranteed not to match anything
4663 in the edge structure being shipped out. On the other hand, the initial color
4664 should be black so that the translation of an all-black picture will have no
4665 \&{setcolor} commands. (These would be undesirable in a font application.)
4666 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4667 to recover from a situation where we have lost track of the graphics state.
4669 @d mp_void (null+1) /* a null pointer different from |null| */
4671 @c static void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4672 struct _gs_state *p; /* to shift graphic states around */
4673 if ( (c==0)||(c==-1) ) {
4674 if ( mp->ps->gs_state==NULL ) {
4675 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4678 while ( gs_previous!=NULL ) {
4680 mp_xfree(mp->ps->gs_state);
4684 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4685 gs_colormodel=mp_uninitialized_model;
4690 gs_dash_init_done=false;
4692 } else if ( c==1 ) {
4693 p= mp->ps->gs_state;
4694 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4695 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4697 } else if ( c==2 ) {
4699 mp_xfree(mp->ps->gs_state);
4705 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4706 that \ps's idea of the graphics state agrees with what is stored in the object.
4709 static void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ;
4712 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) {
4713 /* get ready to output graphical object |p| */
4714 mp_knot *pp, *path_p; /* for list manipulation */
4716 scaled wx,wy,ww; /* dimensions of pen bounding box */
4717 quarterword adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4718 integer tx,ty; /* temporaries for computing |adj_wx| */
4719 if ( gr_has_color(p) )
4720 @<Make sure \ps\ will use the right color for object~|p|@>;
4721 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) {
4722 if (gr_type(p)==mp_fill_code) {
4723 pp = gr_pen_p((mp_fill_object *)p);
4724 path_p = gr_path_p((mp_fill_object *)p);
4726 pp = gr_pen_p((mp_stroked_object *)p);
4727 path_p = gr_path_p((mp_stroked_object *)p);
4730 if ( pen_is_elliptical(pp) ) {
4731 @<Generate \ps\ code that sets the stroke width to the
4732 appropriate rounded value@>;
4733 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4734 @<Decide whether the line cap parameter matters and set it if necessary@>;
4735 @<Set the other numeric parameters as needed for object~|p|@>;
4738 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
4741 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4742 if ( gr_type(p)==mp_stroked_code ) {
4743 mp_stroked_object *ts = (mp_stroked_object *)p;
4744 if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) )
4745 if ( gs_lcap!=(quarterword)gr_lcap_val(ts) ) {
4747 mp_ps_print_char(mp, ' ');
4748 mp_ps_print_char(mp, '0'+gr_lcap_val(ts));
4749 mp_ps_print_cmd(mp, " setlinecap"," lc");
4750 gs_lcap=(quarterword)gr_lcap_val(ts);
4755 @d set_ljoin_miterlim(p)
4756 if ( gs_ljoin!=(quarterword)gr_ljoin_val(p) ) {
4758 mp_ps_print_char(mp, ' ');
4759 mp_ps_print_char(mp, '0'+gr_ljoin_val(p));
4760 mp_ps_print_cmd(mp, " setlinejoin"," lj");
4761 gs_ljoin=(quarterword)gr_ljoin_val(p);
4763 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4765 mp_ps_print_char(mp, ' ');
4766 mp_ps_print_scaled(mp, gr_miterlim_val(p));
4767 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4768 gs_miterlim=gr_miterlim_val(p);
4771 @<Set the other numeric parameters as needed for object~|p|@>=
4772 if ( gr_type(p)==mp_stroked_code ) {
4773 mp_stroked_object *ts = (mp_stroked_object *)p;
4774 set_ljoin_miterlim(ts);
4776 mp_fill_object *ts = (mp_fill_object *)p;
4777 set_ljoin_miterlim(ts);
4781 @d set_color_objects(pq)
4782 object_color_model = pq->color_model;
4783 object_color_a = pq->color.a_val;
4784 object_color_b = pq->color.b_val;
4785 object_color_c = pq->color.c_val;
4786 object_color_d = pq->color.d_val;
4788 @<Make sure \ps\ will use the right color for object~|p|@>=
4790 int object_color_model;
4791 int object_color_a, object_color_b, object_color_c, object_color_d ;
4792 if (gr_type(p) == mp_fill_code) {
4793 mp_fill_object *pq = (mp_fill_object *)p;
4794 set_color_objects(pq);
4795 } else if (gr_type(p) == mp_stroked_code) {
4796 mp_stroked_object *pq = (mp_stroked_object *)p;
4797 set_color_objects(pq);
4799 mp_text_object *pq = (mp_text_object *)p;
4800 set_color_objects(pq);
4803 if ( object_color_model==mp_rgb_model) {
4804 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)||
4805 (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) {
4806 gs_red = object_color_a;
4807 gs_green = object_color_b;
4808 gs_blue = object_color_c;
4810 gs_colormodel=mp_rgb_model;
4812 mp_ps_print_char(mp, ' ');
4813 mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
4814 mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
4815 mp_ps_print_scaled(mp, gs_blue);
4816 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4819 } else if ( object_color_model==mp_cmyk_model) {
4820 if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)||
4821 (gs_blue!=object_color_c)||(gs_black!=object_color_d)||
4822 (gs_colormodel!=mp_cmyk_model) ) {
4823 gs_red = object_color_a;
4824 gs_green = object_color_b;
4825 gs_blue = object_color_c;
4826 gs_black = object_color_d;
4827 gs_colormodel=mp_cmyk_model;
4829 mp_ps_print_char(mp, ' ');
4830 mp_ps_print_scaled(mp, gs_red);
4831 mp_ps_print_char(mp, ' ');
4832 mp_ps_print_scaled(mp, gs_green);
4833 mp_ps_print_char(mp, ' ');
4834 mp_ps_print_scaled(mp, gs_blue);
4835 mp_ps_print_char(mp, ' ');
4836 mp_ps_print_scaled(mp, gs_black);
4837 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4840 } else if ( object_color_model==mp_grey_model ) {
4841 if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) {
4842 gs_red = object_color_a;
4846 gs_colormodel=mp_grey_model;
4848 mp_ps_print_char(mp, ' ');
4849 mp_ps_print_scaled(mp, gs_red);
4850 mp_ps_print_cmd(mp, " setgray"," G");
4853 } else if ( object_color_model==mp_no_model ) {
4854 gs_colormodel=mp_no_model;
4858 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4859 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4860 @:setwidth}{\&{setwidth}command@>
4861 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4862 generate \ps\ code that computes the rounded value. For non-circular pens, the
4863 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4864 have the computed width.
4866 Rounding the width to whole pixels is not likely to improve the appearance of
4867 diagonal or curved strokes, but we do it anyway for consistency. The
4868 \&{truncate} command generated here tends to make all the strokes a little
4869 @:truncate}{\&{truncate} command@>
4870 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4871 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4872 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4875 @<Generate \ps\ code that sets the stroke width...@>=
4876 @<Set |wx| and |wy| to the width and height of the bounding box for
4878 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4879 important and set |adj_wx| and |ww| accordingly@>;
4880 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4881 if ( adj_wx != 0 ) {
4883 mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
4885 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4887 if ( mp->internal[mp_procset]>0 ) {
4889 mp_ps_print_char(mp, ' ');
4890 mp_ps_print_scaled(mp, ww);
4891 mp_ps_print(mp, " vlw");
4894 mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
4895 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4902 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4903 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4904 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4905 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4907 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4908 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4911 @ The path is considered ``essentially horizontal'' if its range of
4912 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4913 vertical'' paths are detected similarly. This code ensures that no component
4914 of the pen transformation is more that |aspect_bound*(ww+1)|.
4916 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4917 more than this factor */
4922 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4924 if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound;
4925 else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound;
4926 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=0; }
4927 else { ww=wx; adj_wx=1; }
4929 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4930 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4931 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4932 bounding-box computation.
4935 static boolean mp_gr_coord_rangeOK (mp_knot *h,
4936 quarterword zoff, scaled dz);
4939 boolean mp_gr_coord_rangeOK (mp_knot *h,
4940 quarterword zoff, scaled dz) {
4941 mp_knot *p; /* for scanning the path form |h| */
4942 scaled zlo,zhi; /* coordinate range so far */
4943 scaled z; /* coordinate currently being tested */
4944 if (zoff==do_x_loc) {
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_x(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 while ( gr_right_type(p)!=mp_endpoint ) {
4963 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4964 p=gr_next_knot(p); z=gr_left_y(p);
4965 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4967 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4974 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4976 else if ( z>zhi ) zhi=z;
4977 if ( zhi-zlo>dz ) return false
4979 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4980 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4981 @:stroke}{\&{stroke} command@>
4982 @:fill}{\&{fill} command@>
4983 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4986 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4987 if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) {
4993 if ( gs_dash_p!=NULL || gs_dash_init_done == false) {
4994 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4996 gs_dash_init_done=true;
4998 } else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) {
4999 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
5002 @ The original code had a check here to ensure that the result from
5003 |mp_take_scaled| did not go out of bounds.
5005 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5007 if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array==NULL)) {
5008 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5012 mp_ps_print(mp, " [");
5013 for (i=0; *(hh->array+i) != -1;i++) {
5015 mp_ps_print_scaled(mp, *(hh->array+i));
5016 mp_ps_print_char(mp, ' ') ;
5019 mp_ps_print(mp, "] ");
5020 mp_ps_print_scaled(mp, hh->offset);
5021 mp_ps_print_cmd(mp, " setdash"," sd");
5026 static boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ;
5028 @ This function test if |h| and |hh| represent the same dash pattern.
5031 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) {
5034 if ( h==hh ) ret=true;
5035 else if ( (h==NULL)||(hh==NULL) ) ret=false;
5036 else if ( h->offset!=hh->offset ) ret=false;
5037 else if ( h->array == hh->array ) ret=true;
5038 else if ( h->array == NULL || hh->array == NULL) ret=false;
5039 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5043 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5045 while (*(h->array+i)!=-1 &&
5046 *(hh->array+i)!=-1 &&
5047 *(h->array+i) == *(hh->array+i)) i++;
5049 if (*(h->array+i)==-1 && *(hh->array+i) == -1)
5054 @ When stroking a path with an elliptical pen, it is necessary to transform
5055 the coordinate system so that a unit circular pen will have the desired shape.
5056 To keep this transformation local, we enclose it in a
5057 $$\&{gsave}\ldots\&{grestore}$$
5058 block. Any translation component must be applied to the path being stroked
5059 while the rest of the transformation must apply only to the pen.
5060 If |fill_also=true|, the path is to be filled as well as stroked so we must
5061 insert commands to do this after giving the path.
5064 static void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) ;
5067 @c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) {
5068 /* generate an elliptical pen stroke from object |h| */
5069 scaled txx,txy,tyx,tyy; /* transformation parameters */
5070 mp_knot *p; /* the pen to stroke with */
5071 scaled d1,det; /* for tweaking transformation parameters */
5072 integer s; /* also for tweaking transformation paramters */
5073 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5075 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5077 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5078 if (gr_type(h)==mp_fill_code) {
5079 mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h));
5081 mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h));
5083 if ( mp->internal[mp_procset]==0 ) {
5084 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5085 @<Issue \ps\ commands to transform the coordinate system@>;
5086 mp_ps_print(mp, " stroke");
5087 if ( transformed ) mp_ps_print(mp, " grestore");
5089 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5090 if ( (txy!=0)||(tyx!=0) ) {
5091 mp_ps_print(mp, " [");
5092 mp_ps_pair_out(mp, txx,tyx);
5093 mp_ps_pair_out(mp, txy,tyy);
5094 mp_ps_print(mp, "0 0] t");
5095 } else if ((txx!=unity)||(tyy!=unity) ) {
5096 mp_ps_pair_out(mp,txx,tyy);
5097 mp_ps_print(mp, " s");
5099 mp_ps_print(mp, " S");
5100 if ( transformed ) mp_ps_print(mp, " Q");
5105 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5106 if (gr_type(h)==mp_fill_code) {
5107 p=gr_pen_p((mp_fill_object *)h);
5109 p=gr_pen_p((mp_stroked_object *)h);
5115 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5116 mp_ps_print_nl(mp, "");
5117 mp_ps_print_cmd(mp, "gsave ","q ");
5118 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5119 mp_ps_print(mp, "translate ");
5126 mp_ps_print_nl(mp, "");
5128 @<Adjust the transformation to account for |gs_width| and output the
5129 initial \&{gsave} if |transformed| should be |true|@>
5131 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5132 if ( gs_width!=unity ) {
5133 if ( gs_width==0 ) {
5134 txx=unity; tyy=unity;
5136 txx=mp_make_scaled(mp, txx,gs_width);
5137 txy=mp_make_scaled(mp, txy,gs_width);
5138 tyx=mp_make_scaled(mp, tyx,gs_width);
5139 tyy=mp_make_scaled(mp, tyy,gs_width);
5142 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5143 if ( (! transformed) ){
5144 mp_ps_print_cmd(mp, "gsave ","q ");
5149 @ @<Issue \ps\ commands to transform the coordinate system@>=
5150 if ( (txy!=0)||(tyx!=0) ){
5152 mp_ps_print_char(mp, '[');
5153 mp_ps_pair_out(mp, txx,tyx);
5154 mp_ps_pair_out(mp, txy,tyy);
5155 mp_ps_print(mp, "0 0] concat");
5156 } else if ( (txx!=unity)||(tyy!=unity) ){
5158 mp_ps_pair_out(mp, txx,tyy);
5159 mp_ps_print(mp, "scale");
5162 @ The \ps\ interpreter will probably abort if it encounters a singular
5163 transformation matrix. The determinant must be large enough to ensure that
5164 the printed representation will be nonsingular. Since the printed
5165 representation is always within $2^{-17}$ of the internal |scaled| value, the
5166 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5167 the magnitudes of |txx/65536|, |txy/65536|, etc.
5169 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5170 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5172 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5173 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5174 d1=4*aspect_bound+1;
5175 if ( abs(det)<d1 ) {
5176 if ( det>=0 ) { d1=d1-det; s=1; }
5177 else { d1=-d1-det; s=-1; };
5179 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5180 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5181 else txx=txx+(d1+s*abs(tyy)) / tyy;
5183 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5184 else txy=txy+(d1+s*abs(tyx)) / tyx;
5188 @ Here is a simple routine that just fills a cycle.
5191 static void mp_gr_ps_fill_out (MP mp, mp_knot *p);
5194 void mp_gr_ps_fill_out (MP mp, mp_knot *p) { /* fill cyclic path~|p| */
5195 mp_gr_ps_path_out(mp, p);
5196 mp_ps_print_cmd(mp, " fill"," F");
5200 @ A text node may specify an arbitrary transformation but the usual case
5201 involves only shifting, scaling, and occasionally rotation. The purpose
5202 of |choose_scale| is to select a scale factor so that the remaining
5203 transformation is as ``nice'' as possible. The definition of ``nice''
5204 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5205 nice because they work out well for bitmap fonts. The code here selects
5206 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5207 non-shifting part of the transformation matrix. It is careful to avoid
5208 additions that might cause undetected overflow.
5211 static scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) ;
5213 @ @c scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) {
5214 /* |p| should point to a text node */
5215 scaled a,b,c,d,ad,bc; /* temporary values */
5220 if ( a<0 ) negate(a);
5221 if ( b<0 ) negate(b);
5222 if ( c<0 ) negate(c);
5223 if ( d<0 ) negate(d);
5226 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5229 @ The potential overflow here is caused by the fact the returned value
5230 has to fit in a |name_type|, which is a quarterword.
5232 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
5235 static quarterword mp_size_index (MP mp, font_number f, scaled s) ;
5238 quarterword mp_size_index (MP mp, font_number f, scaled s) {
5239 pointer p,q; /* the previous and current font size nodes */
5240 quarterword i; /* the size index for |q| */
5241 q=mp->font_sizes[f];
5244 if ( abs(s-sc_factor(q))<=fscale_tolerance )
5247 { p=q; q=link(q); incr(i); };
5248 if ( i==max_quarterword )
5249 mp_overflow(mp, "sizes per font",max_quarterword);
5250 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
5252 q=mp_get_node(mp, font_size_size);
5254 if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
5259 static scaled mp_indexed_size (MP mp,font_number f, quarterword j);
5262 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
5263 pointer p; /* a font size node */
5264 quarterword i; /* the size index for |p| */
5265 p=mp->font_sizes[f];
5267 if ( p==null ) mp_confusion(mp, "size");
5270 if ( p==null ) mp_confusion(mp, "size");
5272 return sc_factor(p);
5276 static void mp_clear_sizes (MP mp) ;
5278 @ @c void mp_clear_sizes (MP mp) {
5279 font_number f; /* the font whose size list is being cleared */
5280 pointer p; /* current font size nodes */
5281 for (f=null_font+1;f<=mp->last_fnum;f++) {
5282 while ( mp->font_sizes[f]!=null ) {
5283 p=mp->font_sizes[f];
5284 mp->font_sizes[f]=link(p);
5285 mp_free_node(mp, p,font_size_size);
5290 @ A text node may specify an arbitrary transformation but the usual case
5291 involves only shifting, scaling, and occasionally rotation. The purpose
5292 of |choose_scale| is to select a scale factor so that the remaining
5293 transformation is as ``nice'' as possible. The definition of ``nice''
5294 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5295 nice because they work out well for bitmap fonts. The code here selects
5296 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5297 non-shifting part of the transformation matrix. It is careful to avoid
5298 additions that might cause undetected overflow.
5301 @ @c static scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
5302 /* |p| should point to a text node */
5303 scaled a,b,c,d,ad,bc; /* temporary values */
5308 if ( (a<0) ) negate(a);
5309 if ( (b<0) ) negate(b);
5310 if ( (c<0) ) negate(c);
5311 if ( (d<0) ) negate(d);
5314 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5317 @ There may be many sizes of one font and we need to keep track of the
5318 characters used for each size. This is done by keeping a linked list of
5319 sizes for each font with a counter in each text node giving the appropriate
5320 position in the size list for its font.
5322 @d font_size_size 2 /* size of a font size node */
5326 static void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size);
5329 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) {
5330 mp_graphic_object * p;
5332 while ( p!= NULL ) {
5333 if ( gr_type(p)==mp_text_code ) {
5334 if ( gr_font_n(p)!=null_font ) {
5335 if ( gr_size_index(p)==(unsigned char)next_size )
5336 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p));
5343 @ @<Unmark all marked characters@>=
5344 for (f=null_font+1;f<=mp->last_fnum;f++) {
5345 if ( mp->font_sizes[f]!=null ) {
5346 mp_unmark_font(mp, f);
5347 mp->font_sizes[f]=null;
5351 @ @<Scan all the text nodes and mark the used ...@>=
5354 if ( gr_type(p)==mp_text_code ) {
5356 if (f!=null_font ) {
5357 switch (prologues) {
5360 mp->font_sizes[f] = mp_void;
5361 mp_mark_string_chars(mp, f, gr_text_p(p));
5362 if (mp_has_fm_entry(mp,f,NULL) ) {
5363 if (mp->font_enc_name[f]==NULL )
5364 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
5365 mp->font_ps_name[f] = mp_fm_font_name(mp,f);
5369 mp->font_sizes[f]=mp_void;
5372 gr_size_index(p)=(unsigned char)mp_size_index(mp, f,mp_choose_scale(mp, p));
5373 if ( gr_size_index(p)==0 )
5374 mp_mark_string_chars(mp, f, gr_text_p(p));
5383 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5385 @<Exported function ...@>=
5386 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) ;
5389 int mp_gr_ship_out (mp_edge_object *hh, int qprologues, int qprocset,int standalone) {
5390 mp_graphic_object *p;
5391 scaled ds,scf; /* design size and scale factor for a text node */
5392 font_number f; /* for loops over fonts while (un)marking characters */
5393 boolean transformed; /* is the coordinate system being transformed? */
5394 int prologues, procset;
5397 mp->jump_buf = malloc(sizeof(jmp_buf));
5398 if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)))
5401 if (mp->history >= mp_fatal_error_stop ) return 1;
5403 prologues = (int)((unsigned)mp->internal[mp_prologues]>>16);
5405 prologues=qprologues;
5407 procset = (int)((unsigned)mp->internal[mp_procset]>>16);
5410 mp_open_output_file(mp);
5411 mp_print_initial_comment(mp, hh, prologues);
5413 @<Unmark all marked characters@>;
5414 if ( prologues==2 || prologues==3 ) {
5415 mp_reload_encodings(mp);
5417 @<Scan all the text nodes and mark the used characters@>;
5418 if ( prologues==2 || prologues==3 ) {
5419 mp_print_improved_prologue(mp, hh, prologues, procset);
5421 mp_print_prologue(mp, hh, prologues, procset);
5423 mp_gs_unknown_graphics_state(mp, 0);
5426 if ( gr_has_color(p) ) {
5427 @<Write |pre_script| of |p|@>;
5429 mp_gr_fix_graphics_state(mp, p);
5430 switch (gr_type(p)) {
5432 if ( gr_pen_p((mp_fill_object *)p)==NULL ) {
5433 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5434 } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) ) {
5435 mp_gr_stroke_ellipse(mp, p,true);
5437 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5438 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5440 if ( gr_post_script((mp_fill_object *)p)!=NULL ) {
5441 mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p));
5445 case mp_stroked_code:
5446 if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) )
5447 mp_gr_stroke_ellipse(mp, p,false);
5449 mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p));
5451 if ( gr_post_script((mp_stroked_object *)p)!=NULL ) {
5452 mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p));
5457 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5459 scf=mp_gr_choose_scale(mp, p);
5461 scf=mp_indexed_size(mp, gr_font_n(p), (quarterword)gr_size_index(p));
5462 @<Shift or transform as necessary before outputting text node~|p| at scale
5463 factor~|scf|; set |transformed:=true| if the original transformation must
5465 mp_ps_string_out(mp, gr_text_p(p));
5466 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5467 @<Print the size information and \ps\ commands for text node~|p|@>;
5470 if ( gr_post_script((mp_text_object *)p)!=NULL ) {
5471 mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp);
5474 case mp_start_clip_code:
5475 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5476 mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p));
5477 mp_ps_print_cmd(mp, " clip"," W");
5479 if ( mp->internal[mp_restore_clip_color]>0 )
5480 mp_gs_unknown_graphics_state(mp, 1);
5482 case mp_stop_clip_code:
5483 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5485 if ( mp->internal[mp_restore_clip_color]>0 )
5486 mp_gs_unknown_graphics_state(mp, 2);
5488 mp_gs_unknown_graphics_state(mp, -1);
5490 case mp_start_bounds_code:
5491 case mp_stop_bounds_code:
5493 case mp_special_code:
5495 mp_special_object *ps = (mp_special_object *)p;
5496 mp_ps_print_nl (mp, gr_pre_script(ps));
5497 mp_ps_print_ln (mp);
5500 } /* all cases are enumerated */
5503 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
5504 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
5505 (mp->close_file)(mp,mp->ps_file);
5512 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) ;
5515 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) {
5516 return mp_gr_ship_out (hh, prologues, procset, (int)true);
5524 @d do_write_prescript(a,b) {
5525 if ( (gr_pre_script((b *)a))!=NULL ) {
5526 mp_ps_print_nl (mp, gr_pre_script((b *)a));
5531 @<Write |pre_script| of |p|@>=
5533 if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); }
5534 else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); }
5535 else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); }
5538 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5540 mp_ps_print_char(mp, ' ');
5541 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5542 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5543 mp_ps_print(mp, " fshow");
5545 mp_ps_print_cmd(mp, " grestore"," Q")
5549 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5550 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5551 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5552 if ( transformed ) {
5553 mp_ps_print_cmd(mp, "gsave [", "q [");
5554 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5555 mp_make_scaled(mp, gr_tyx_val(p),scf));
5556 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5557 mp_make_scaled(mp, gr_tyy_val(p),scf));
5558 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5559 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5561 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5562 mp_ps_print_cmd(mp, "moveto","m");
5567 void mp_gr_toss_objects ( mp_edge_object *hh) ;
5568 void mp_gr_toss_object (mp_graphic_object *p) ;
5571 void mp_gr_toss_object (mp_graphic_object *p) {
5573 mp_stroked_object *ts;
5575 switch (gr_type(p)) {
5577 tf = (mp_fill_object *)p;
5578 mp_xfree(gr_pre_script(tf));
5579 mp_xfree(gr_post_script(tf));
5580 mp_gr_toss_knot_list(mp,gr_pen_p(tf));
5581 mp_gr_toss_knot_list(mp,gr_path_p(tf));
5582 mp_gr_toss_knot_list(mp,gr_htap_p(p));
5584 case mp_stroked_code:
5585 ts = (mp_stroked_object *)p;
5586 mp_xfree(gr_pre_script(ts));
5587 mp_xfree(gr_post_script(ts));
5588 mp_gr_toss_knot_list(mp,gr_pen_p(ts));
5589 mp_gr_toss_knot_list(mp,gr_path_p(ts));
5590 if (gr_dash_p(p)!=NULL)
5591 mp_gr_toss_dashes (mp,gr_dash_p(p));
5594 tt = (mp_text_object *)p;
5595 mp_xfree(gr_pre_script(tt));
5596 mp_xfree(gr_post_script(tt));
5597 mp_xfree(gr_text_p(p));
5598 mp_xfree(gr_font_name(p));
5600 case mp_start_clip_code:
5601 mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p));
5603 case mp_start_bounds_code:
5604 mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p));
5606 case mp_stop_clip_code:
5607 case mp_stop_bounds_code:
5609 case mp_special_code:
5610 mp_xfree(gr_pre_script((mp_special_object *)p));
5612 } /* all cases are enumerated */
5618 void mp_gr_toss_objects (mp_edge_object *hh) {
5619 mp_graphic_object *p, *q;
5623 mp_gr_toss_object(p);
5630 mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ;
5634 mp_gr_copy_object (MP mp, mp_graphic_object *p) {
5636 mp_stroked_object *ts;
5639 mp_bounds_object *tb;
5640 mp_special_object *tp;
5641 mp_graphic_object *q = NULL;
5642 switch (gr_type(p)) {
5644 tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code);
5645 gr_pre_script(tf) = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p));
5646 gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p));
5647 gr_path_p(tf) = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p));
5648 gr_htap_p(tf) = mp_gr_copy_path(mp,gr_htap_p(p));
5649 gr_pen_p(tf) = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p));
5650 q = (mp_graphic_object *)tf;
5652 case mp_stroked_code:
5653 ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code);
5654 gr_pre_script(ts) = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p));
5655 gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p));
5656 gr_path_p(ts) = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p));
5657 gr_pen_p(ts) = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p));
5658 gr_dash_p(ts) = mp_gr_copy_dashes(mp,gr_dash_p(p));
5659 q = (mp_graphic_object *)ts;
5662 tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code);
5663 gr_pre_script(tt) = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p));
5664 gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p));
5665 gr_text_p(tt) = mp_xstrdup(mp, gr_text_p(p));
5666 gr_font_name(tt) = mp_xstrdup(mp, gr_font_name(p));
5667 q = (mp_graphic_object *)tt;
5669 case mp_start_clip_code:
5670 tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code);
5671 gr_path_p(tc) = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p));
5672 q = (mp_graphic_object *)tc;
5674 case mp_start_bounds_code:
5675 tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code);
5676 gr_path_p(tb) = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p));
5677 q = (mp_graphic_object *)tb;
5679 case mp_special_code:
5680 tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code);
5681 gr_pre_script(tp) = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p));
5682 q = (mp_graphic_object *)tp;
5684 case mp_stop_clip_code:
5685 q = mp_new_graphic_object(mp, mp_stop_clip_code);
5687 case mp_stop_bounds_code:
5688 q = mp_new_graphic_object(mp, mp_stop_bounds_code);
5690 } /* all cases are enumerated */