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| */
80 #include "mplibps.h" /* external header */
81 #include "mpmp.h" /* internal header */
82 #include "mppsout.h" /* internal header */
85 @<Static variables in the outer block@>
87 @ There is a small bit of code from the backend that bleads through
88 to the frontend because I do not know how to set up the includes
89 properly. Those are the definitions of |struct libavl_allocator|
90 and |typedef struct psout_data_struct * psout_data|.
92 The |libavl_allocator| is a trick that makes sure that frontends
93 do not need |avl.h|, and the |psout_data| is needed for the backend
98 typedef struct psout_data_struct {
100 } psout_data_struct ;
101 @<Exported function headers@>
104 static boolean mp_isdigit (int a) {
105 return (a>='0'&&a<='9');
107 static int mp_tolower (int a) {
108 if (a>='A' && a <='Z')
109 return a - 'A' + 'a';
112 static int mp_strcasecmp (const char *s1, const char *s2) {
118 *c = mp_tolower(*c); c++;
123 *c = mp_tolower(*c); c++;
126 free (ss1); free(ss2);
130 @ @<Exported function headers@>=
131 void mp_backend_initialize (MP mp) ;
132 void mp_backend_free (MP mp) ;
135 @c void mp_backend_initialize (MP mp) {
136 mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
137 @<Set initial values@>;
139 void mp_backend_free (MP mp) {
140 @<Dealloc variables@>;
148 @ Writing to ps files
152 /* the number of characters on the current \ps\ file line */
154 @ @<Set initial values@>=
155 mp->ps->ps_offset = 0;
159 @d wps(A) (mp->write_ascii_file)(mp,mp->ps_file,(A))
163 (mp->write_ascii_file)(mp,mp->ps_file,(char *)ss);
165 @d wps_cr (mp->write_ascii_file)(mp,mp->ps_file,"\n")
166 @d wps_ln(A) { wterm_cr; (mp->write_ascii_file)(mp,mp->ps_file,(A)); }
169 static void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
175 static void mp_ps_print_char (MP mp, int s) { /* prints a single character */
177 wps_cr; mp->ps->ps_offset=0;
179 wps_chr(s); incr(mp->ps->ps_offset);
184 static void mp_ps_do_print (MP mp, const char *ss, size_t len) { /* prints string |s| */
187 mp_ps_print_char(mp, ss[j]); incr(j);
191 @ Deciding where to break the ps output line.
193 @d ps_room(A) if ( (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) {
194 mp_ps_print_ln(mp); /* optional line break */
198 static void mp_ps_print (MP mp, const char *ss) {
200 mp_ps_do_print(mp, ss, strlen(ss));
203 @ The procedure |print_nl| is like |print|, but it makes sure that the
204 string appears at the beginning of a new line.
207 static void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */
208 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
212 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
215 static void mp_ps_print_the_digs (MP mp, int k) {
216 /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
218 mp_ps_print_char(mp, '0'+mp->dig[k]);
222 @ The following procedure, which prints out the decimal representation of a
223 given integer |n|, has been written carefully so that it works properly
224 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
225 to negative arguments, since such operations are not implemented consistently
226 by all \PASCAL\ compilers.
229 static void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */
230 integer m; /* used to negate |n| in possibly dangerous cases */
231 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
233 mp_ps_print_char(mp, '-');
234 if ( n>-100000000 ) {
237 m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
239 mp->dig[0]=(unsigned char)m;
241 mp->dig[0]=0; incr(n);
246 mp->dig[k]=(unsigned char)(n % 10); n=n / 10; incr(k);
248 mp_ps_print_the_digs(mp, k);
251 @ \MP\ also makes use of a trivial procedure to print two digits. The
252 following subroutine is usually called with a parameter in the range |0<=n<=99|.
255 static void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */
257 mp_ps_print_char(mp, '0'+(n / 10));
258 mp_ps_print_char(mp, '0'+(n % 10));
261 @ Conversely, here is a procedure analogous to |print_int|. If the output
262 of this procedure is subsequently read by \MP\ and converted by the
263 |round_decimals| routine above, it turns out that the original value will
264 be reproduced exactly. A decimal point is printed only if the value is
265 not an integer. If there is more than one way to print the result with
266 the optimum number of digits following the decimal point, the closest
267 possible value is given.
269 The invariant relation in the \&{repeat} loop is that a sequence of
270 decimal digits yet to be printed will yield the original number if and only if
271 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
272 We can stop if and only if $f=0$ satisfies this condition; the loop will
273 terminate before $s$ can possibly become zero.
276 static void mp_ps_print_scaled (MP mp,scaled s) {
277 scaled delta; /* amount of allowable inaccuracy */
279 mp_ps_print_char(mp, '-');
280 negate(s); /* print the sign, if negative */
282 mp_ps_print_int(mp, s / unity); /* print the integer part */
286 mp_ps_print_char(mp, '.');
289 s=s+0100000-(delta / 2); /* round the final digit */
290 mp_ps_print_char(mp, '0'+(s / unity));
298 @* \[44a] Dealing with font encodings.
300 First, here are a few helpers for parsing files
302 @d check_buf(size, buf_size)
303 if ((unsigned)(size) > (unsigned)(buf_size)) {
305 mp_snprintf(S,128,"buffer overflow: (%d,%d) at file %s, line %d",
306 size,buf_size, __FILE__, __LINE__ );
307 mp_fatal_error(mp,S);
310 @d append_char_to_buf(c, p, buf, buf_size) do {
313 if (c == 13 || c == EOF)
315 if (c != ' ' || (p > buf && p[-1] != 32)) {
316 check_buf(p - buf + 1, (buf_size));
321 @d append_eol(p, buf, buf_size) do {
322 check_buf(p - buf + 2, (buf_size));
323 if (p - buf > 1 && p[-1] != 10)
325 if (p - buf > 2 && p[-2] == 32) {
332 @d remove_eol(p, buf) do {
338 @d skip(p, c) if (*p == c) p++
339 @d strend(s) strchr(s, 0)
340 @d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
345 boolean loaded; /* the encoding has been loaded? */
346 char *file_name; /* encoding file name */
347 char *enc_name; /* encoding true name */
348 integer objnum; /* object number */
350 integer tounicode; /* object number of associated ToUnicode entry */
360 #define ENC_BUF_SIZE 0x1000
361 char enc_line[ENC_BUF_SIZE];
365 @d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file)
366 @d enc_close() (mp->close_file)(mp,mp->ps->enc_file)
369 static int enc_getchar(MP mp) {
371 unsigned char abyte=0;
372 void *byte_ptr = &abyte;
373 (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len);
378 static boolean mp_enc_open (MP mp, char *n) {
379 mp->ps->enc_file=(mp->open_file)(mp,n, "r", mp_filetype_encoding);
380 if (mp->ps->enc_file!=NULL)
385 static void mp_enc_getline (MP mp) {
390 print_err("unexpected end of file");
393 p = mp->ps->enc_line;
395 c = enc_getchar (mp);
396 append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
398 append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
399 if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
402 static void mp_load_enc (MP mp, char *enc_name,
403 char **enc_encname, char **glyph_names){
404 char buf[ENC_BUF_SIZE], *p, *r;
407 unsigned save_selector = mp->selector;
408 if (!mp_enc_open (mp,enc_name)) {
409 mp_print (mp,"cannot open encoding file for reading");
412 mp_normalize_selector(mp);
414 mp_print (mp, enc_name);
416 if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
417 remove_eol (r, mp->ps->enc_line);
418 print_err ("invalid encoding vector (a name or `[' missing): `");
419 mp_print(mp,mp->ps->enc_line);
423 while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
424 myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
425 memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
426 *(myname+(r-mp->ps->enc_line-1))=0;
427 *enc_encname = myname;
435 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
438 if (names_count > 256) {
439 print_err ("encoding vector contains more than 256 names");
442 if (mp_xstrcmp (buf, notdef) != 0)
443 glyph_names[names_count] = mp_xstrdup (mp,buf);
446 if (*r != 10 && *r != '%') {
447 if (str_prefix (r, "] def"))
450 remove_eol (r, mp->ps->enc_line);
452 ("invalid encoding vector: a name or `] def' expected: `");
453 mp_print(mp,mp->ps->enc_line);
459 r = mp->ps->enc_line;
464 mp->selector = save_selector;
466 static void mp_read_enc (MP mp, enc_entry * e) {
470 mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
474 @ |write_enc| is used to write either external encoding (given in map file) or
475 internal encoding (read from the font file);
476 the 2nd argument is a pointer to the encoding entry;
479 static void mp_write_enc (MP mp, enc_entry * e) {
483 if (e->objnum != 0) /* the encoding has been written already */
488 mp_ps_print(mp,"\n%%%%BeginResource: encoding ");
489 mp_ps_print(mp, e->enc_name);
490 mp_ps_print_nl(mp, "/");
491 mp_ps_print(mp, e->enc_name);
492 mp_ps_print(mp, " [ ");
493 foffset = strlen(e->file_name)+3;
494 for (i = 0; i < 256; i++) {
496 if (s+1+foffset>=80) {
501 mp_ps_print_char(mp,'/');
502 mp_ps_print(mp, g[i]);
503 mp_ps_print_char(mp,' ');
507 mp_ps_print_nl (mp,"] def\n");
508 mp_ps_print(mp,"%%%%EndResource");
512 @ All encoding entries go into AVL tree for fast search by name.
515 struct avl_table *enc_tree;
517 @ Memory management functions for avl
519 @<Static variables in the outer block@>=
520 static const char notdef[] = ".notdef";
523 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
524 static void avl_xfree (struct libavl_allocator *allocator, void *block);
527 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
529 return malloc (size);
531 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
537 struct libavl_allocator avl_xallocator;
539 @ @<Set initial...@>=
540 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
541 mp->ps->avl_xallocator.libavl_free= avl_xfree;
542 mp->ps->enc_tree = NULL;
545 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
547 return strcmp (((const enc_entry *) pa)->file_name,
548 ((const enc_entry *) pb)->file_name);
550 static enc_entry * mp_add_enc (MP mp, char *s) {
554 if (mp->ps->enc_tree == NULL) {
555 mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
558 p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
559 if (p != NULL) /* encoding already registered */
561 p = mp_xmalloc (mp,1,sizeof (enc_entry));
563 p->file_name = mp_xstrdup (mp,s);
566 p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
567 for (i = 0; i < 256; i++)
568 p->glyph_names[i] = (char *) notdef;
569 aa = avl_probe (mp->ps->enc_tree, p);
576 static void mp_destroy_enc_entry (void *pa, void *pb) {
579 p = (enc_entry *) pa;
581 mp_xfree (p->file_name);
582 if (p->glyph_names != NULL)
583 for (i = 0; i < 256; i++)
584 if (p->glyph_names[i] != notdef)
585 mp_xfree (p->glyph_names[i]);
586 mp_xfree (p->glyph_names);
591 static void enc_free (MP mp);
593 @ @c static void enc_free (MP mp) {
594 if (mp->ps->enc_tree != NULL)
595 avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
599 static void mp_reload_encodings (MP mp) ;
600 static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) ;
602 @ @c void mp_reload_encodings (MP mp) {
606 font_number lastfnum = mp->last_fnum;
607 for (f=null_font+1;f<=lastfnum;f++) {
608 if (mp->font_enc_name[f]!=NULL ) {
609 mp_xfree(mp->font_enc_name[f]);
610 mp->font_enc_name[f]=NULL;
612 if (mp_has_fm_entry (mp,f,&fm_cur)) {
613 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
614 e = fm_cur->encoding;
620 static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) {
624 for (f=null_font+1;f<=lastfnum;f++) {
625 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
626 if (fm != NULL && (fm->ps_name != NULL)) {
627 if (is_reencoded (fm)) {
628 if (encodings_only || (!is_subsetted (fm))) {
630 mp_write_enc (mp, e);
631 /* clear for next run */
640 @* \[44b] Parsing font map files.
648 @d fm_close() (mp->close_file)(mp,mp->ps->fm_file)
649 @d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file)
652 static int fm_getchar(MP mp) {
654 unsigned char abyte=0;
655 void *byte_ptr = &abyte;
656 (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
664 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
665 enum _ltype { MAPFILE, MAPLINE };
666 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
667 typedef struct mitem {
668 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
669 int type; /* map file or map line */
670 char *map_line; /* pointer to map file name or map line */
671 int lineno; /* line number in map file */
677 fm_entry *loaded_tfm_found;
678 fm_entry *avail_tfm_found;
679 fm_entry *non_tfm_found;
680 fm_entry *not_avail_tfm_found;
682 @ @<Set initial...@>=
683 mp->ps->mitem = NULL;
686 static const char nontfm[] = "<nontfm>";
689 @d read_field(r, q, buf) do {
691 while (*r != ' ' && *r != '\0')
699 fm->F = mp_xstrdup(mp,buf);
711 static fm_entry *new_fm_entry (MP mp) {
713 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
718 fm->subset_tag = NULL;
720 fm->tfm_num = null_font;
721 fm->tfm_avail = TFM_UNCHECKED;
729 fm->all_glyphs = false;
736 static void delete_fm_entry (fm_entry * fm) {
737 mp_xfree (fm->tfm_name);
738 mp_xfree (fm->ps_name);
739 mp_xfree (fm->ff_name);
740 mp_xfree (fm->subset_tag);
741 mp_xfree (fm->charset);
745 static ff_entry *new_ff_entry (MP mp) {
747 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
753 static void delete_ff_entry (ff_entry * ff) {
754 mp_xfree (ff->ff_name);
755 mp_xfree (ff->ff_path);
759 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
760 static char buf[SMALL_BUF_SIZE];
761 char *p = tfmname, *r = strend (p) - 1, *q = r;
762 while (q > p && mp_isdigit (*q))
764 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
766 check_buf (q - p + 1, SMALL_BUF_SIZE);
767 strncpy (buf, p, (size_t) (q - p));
774 static boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
777 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
778 fm_entry *res = NULL;
779 res = mp_fm_lookup (mp, f);
783 return (res != NULL);
787 struct avl_table *tfm_tree;
788 struct avl_table *ps_tree;
789 struct avl_table *ff_tree;
791 @ @<Set initial...@>=
792 mp->ps->tfm_tree = NULL;
793 mp->ps->ps_tree = NULL;
794 mp->ps->ff_tree = NULL;
796 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
799 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
801 return strcmp (((const fm_entry *) pa)->tfm_name,
802 ((const fm_entry *) pb)->tfm_name);
805 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
807 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
809 const fm_entry *p1 = (const fm_entry *) pa;
810 const fm_entry *p2 = (const fm_entry *) pb;
812 assert (p1->ps_name != NULL && p2->ps_name != NULL);
813 if ((i = strcmp (p1->ps_name, p2->ps_name)))
815 cmp_return (p1->slant, p2->slant);
816 cmp_return (p1->extend, p2->extend);
817 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
818 (i = strcmp (p1->tfm_name, p2->tfm_name)))
823 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
825 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
827 return strcmp (((const ff_entry *) pa)->ff_name,
828 ((const ff_entry *) pb)->ff_name);
831 @ @c static void create_avl_trees (MP mp) {
832 if (mp->ps->tfm_tree == NULL) {
833 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
834 assert (mp->ps->tfm_tree != NULL);
836 if (mp->ps->ps_tree == NULL) {
837 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
838 assert (mp->ps->ps_tree != NULL);
840 if (mp->ps->ff_tree == NULL) {
841 mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
842 assert (mp->ps->ff_tree != NULL);
846 @ The function |avl_do_entry| is not completely symmetrical with regards
847 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
848 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
849 with the original version.
853 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
854 @d set_pslink(fm) ((fm)->links |= LINK_PS)
855 @d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
856 @d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
857 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
858 @d has_pslink(fm) ((fm)->links & LINK_PS)
861 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
867 /* handle |tfm_name| link */
869 if (strcmp (fp->tfm_name, nontfm)) {
870 p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
872 if (mode == FM_DUPIGNORE) {
873 mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
877 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
878 if (mp_has_font_size(mp,p->tfm_num)) {
880 "fontmap entry for `%s' has been used, replace/delete not allowed",
885 a = avl_delete (mp->ps->tfm_tree, p);
892 if (mode != FM_DELETE) {
893 aa = avl_probe (mp->ps->tfm_tree, fp);
899 /* handle |ps_name| link */
901 if (fp->ps_name != NULL) {
902 assert (fp->tfm_name != NULL);
903 p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
905 if (mode == FM_DUPIGNORE) {
907 "ps_name entry for `%s' already exists, duplicates ignored",
911 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
912 if (mp_has_font_size(mp,p->tfm_num)) {
913 /* REPLACE/DELETE not allowed */
915 "fontmap entry for `%s' has been used, replace/delete not allowed",
920 a = avl_delete (mp->ps->ps_tree, p);
923 if (!has_tfmlink (p))
927 if (mode != FM_DELETE) {
928 aa = avl_probe (mp->ps->ps_tree, fp);
934 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
935 return 1; /* deallocation of |fm_entry| structure required */
940 @ consistency check for map entry, with warn flag
943 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
947 if (fm->ps_name != NULL) {
948 if (is_basefont (fm)) {
949 if (is_fontfile (fm) && !is_included (fm)) {
951 mp_snprintf(s,128, "invalid entry for `%s': "
952 "font file must be included or omitted for base fonts",
958 } else { /* not a base font */
959 /* if no font file given, drop this entry */
960 /* |if (!is_fontfile (fm)) {
963 "invalid entry for `%s': font file missing",
972 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
975 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
981 if ((fm->slant != 0 || fm->extend != 0) &&
982 (is_truetype (fm))) {
985 "invalid entry for `%s': "
986 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
992 if (abs (fm->slant) > 1000) {
995 "invalid entry for `%s': too big value of SlantFont (%d/1000.0)",
996 fm->tfm_name, (int)fm->slant);
1001 if (abs (fm->extend) > 2000) {
1004 "invalid entry for `%s': too big value of ExtendFont (%d/1000.0)",
1005 fm->tfm_name, (int)fm->extend);
1010 if (fm->pid != -1 &&
1011 !(is_truetype (fm) && is_included (fm) &&
1012 is_subsetted (fm) && !is_reencoded (fm))) {
1015 "invalid entry for `%s': "
1016 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
1025 @ returns true if s is one of the 14 std. font names; speed-trimmed.
1027 @c static boolean check_basefont (char *s) {
1028 static const char *basefont_names[] = {
1029 "Courier", /* 0:7 */
1030 "Courier-Bold", /* 1:12 */
1031 "Courier-Oblique", /* 2:15 */
1032 "Courier-BoldOblique", /* 3:19 */
1033 "Helvetica", /* 4:9 */
1034 "Helvetica-Bold", /* 5:14 */
1035 "Helvetica-Oblique", /* 6:17 */
1036 "Helvetica-BoldOblique", /* 7:21 */
1038 "Times-Roman", /* 9:11 */
1039 "Times-Bold", /* 10:10 */
1040 "Times-Italic", /* 11:12 */
1041 "Times-BoldItalic", /* 12:16 */
1042 "ZapfDingbats" /* 13:12 */
1044 static const int Index[] =
1045 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
1048 const size_t n = strlen (s);
1052 if (n == 12) { /* three names have length 12 */
1055 k = 1; /* Courier-Bold */
1058 k = 11; /* Times-Italic */
1061 k = 13; /* ZapfDingbats */
1068 if (k > -1 && !strcmp (basefont_names[k], s))
1074 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1076 @c static void fm_scan_line (MP mp) {
1077 int a, b, c, j, u = 0, v = 0;
1080 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1081 char *p, *q, *r, *s;
1083 switch (mp->ps->mitem->type) {
1087 c = fm_getchar (mp);
1088 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1094 r = mp->ps->mitem->map_line;
1099 if (*r == '\0' || is_cfg_comment (*r))
1101 fm = new_fm_entry (mp);
1102 read_field (r, q, buf);
1103 set_field (tfm_name);
1105 read_field (r, q, buf);
1106 if (*buf != '<' && *buf != '"')
1107 set_field (ps_name);
1109 r = p; /* unget the field */
1110 if (mp_isdigit (*r)) { /* font flags given */
1111 fm->flags = atoi (r);
1112 while (mp_isdigit (*r))
1115 while (1) { /* loop through "specials", encoding, font file */
1120 case '"': /* opening quote */
1125 if (sscanf (r, "%f %n", &d, &j) > 0) {
1126 s = r + j; /* jump behind number, eat also blanks, if any */
1127 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1128 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1129 if (str_prefix (s, "SlantFont")) {
1130 d *= 1000.0; /* correct rounding also for neg. numbers */
1131 fm->slant = (short int) (d > 0 ? d + 0.5 : d - 0.5);
1132 r = s + strlen ("SlantFont");
1133 } else if (str_prefix (s, "ExtendFont")) {
1135 fm->extend = (short int) (d > 0 ? d + 0.5 : d - 0.5);
1136 if (fm->extend == 1000)
1138 r = s + strlen ("ExtendFont");
1139 } else { /* unknown name */
1141 *r != ' ' && *r != '"' && *r != '\0';
1142 r++); /* jump over name */
1143 c = *r; /* remember char for temporary end of string */
1145 mp_snprintf(warn_s,128,
1146 "invalid entry for `%s': unknown name `%s' ignored",
1152 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1155 if (*r == '"') /* closing quote */
1158 mp_snprintf(warn_s,128,
1159 "invalid entry for `%s': closing quote missing",
1165 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1166 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1167 fm->pid = (short int)a;
1168 fm->eid = (short int)b;
1172 default: /* encoding or font file specification */
1176 if (*r == '<' || *r == '[')
1179 read_field (r, q, buf);
1180 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1181 if (strlen (buf) > 4 && mp_strcasecmp (strend (buf) - 4, ".enc") == 0) {
1182 fm->encoding = mp_add_enc (mp, buf);
1183 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1184 } else if (strlen (buf) > 0) { /* file name given */
1185 /* font file, formats:
1186 * subsetting: '<cmr10.pfa'
1187 * no subsetting: '<<cmr10.pfa'
1188 * no embedding: 'cmr10.pfa'
1190 if (a == '<' || u == '<') {
1192 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1194 /* otherwise b == '<' (or '[') => no subsetting */
1196 set_field (ff_name);
1205 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1207 if (is_fontfile (fm)
1208 && mp_strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1210 if (check_fm_entry (mp,fm, true) != 0)
1213 Until here the map line has been completely scanned without errors;
1214 fm points to a valid, freshly filled-out |fm_entry| structure.
1215 Now follows the actual work of registering/deleting.
1217 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
1220 delete_fm_entry (fm);
1224 @c static void fm_read_info (MP mp) {
1227 if (mp->ps->tfm_tree == NULL)
1228 create_avl_trees (mp);
1229 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1231 mp->ps->mitem->lineno = 1;
1232 switch (mp->ps->mitem->type) {
1234 n = mp->ps->mitem->map_line;
1235 mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
1236 if (!mp->ps->fm_file) {
1237 mp_snprintf(s,256,"cannot open font map file %s",n);
1240 unsigned save_selector = mp->selector;
1241 mp_normalize_selector(mp);
1244 while (!fm_eof ()) {
1246 mp->ps->mitem->lineno++;
1250 mp->selector = save_selector;
1251 mp->ps->fm_file = NULL;
1261 mp->ps->mitem->map_line = NULL; /* done with this line */
1265 @ @c static void init_fm (fm_entry * fm, font_number f) {
1266 if (fm->tfm_num == null_font ) {
1268 fm->tfm_avail = TFM_FOUND;
1273 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1276 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1281 if (mp->ps->tfm_tree == NULL)
1282 fm_read_info (mp); /* only to read default map file */
1283 tfm = mp->font_name[f];
1284 assert (strcmp (tfm, nontfm));
1285 /* Look up for full <tfmname>[+-]<expand> */
1287 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1290 return (fm_entry *) fm;
1292 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1293 if (tfm == NULL) /* not an expanded font, nothing to do */
1297 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1298 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1299 return (fm_entry *) fm; /* font expansion uses the base font */
1304 @ Early check whether a font file exists. Used e. g. for replacing fonts
1305 of embedded PDF files: Without font file, the font within the embedded
1306 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1307 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1308 only once per font file name + expansion parameter. This might help
1309 keeping speed, if many PDF pages with same fonts are to be embedded.
1311 The |ff_tree| contains only font files, which are actually needed,
1312 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1315 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1320 assert (fm->ff_name != NULL);
1321 tmp.ff_name = fm->ff_name;
1322 ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1323 if (ff == NULL) { /* not yet in database */
1324 ff = new_ff_entry (mp);
1325 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1326 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1327 aa = avl_probe (mp->ps->ff_tree, ff);
1328 assert (aa != NULL);
1333 @ Process map file given by its name or map line contents. Items not
1334 beginning with [+-=] flush default map file, if it has not yet been
1335 read. Leading blanks and blanks immediately following [+-=] are ignored.
1338 @c static void mp_process_map_item (MP mp, char *s, int type) {
1342 s++; /* ignore leading blank */
1344 case '+': /* +mapfile.map, +mapline */
1345 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1348 case '=': /* =mapfile.map, =mapline */
1349 mode = FM_REPLACE; /* try to replace earlier entry */
1352 case '-': /* -mapfile.map, -mapline */
1353 mode = FM_DELETE; /* try to delete entry */
1357 mode = FM_DUPIGNORE; /* like +, but also: */
1358 mp_xfree(mp->ps->mitem->map_line);
1359 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1362 s++; /* ignore blank after [+-=] */
1363 p = s; /* map item starts here */
1365 case MAPFILE: /* remove blank at end */
1366 while (*p != '\0' && *p != ' ')
1370 case MAPLINE: /* blank at end allowed */
1375 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1377 if (*s != '\0') { /* only if real item to process */
1378 mp->ps->mitem->mode = mode;
1379 mp->ps->mitem->type = type;
1380 mp->ps->mitem->map_line = s;
1385 @ @<Exported function headers@>=
1386 void mp_map_file (MP mp, str_number t);
1387 void mp_map_line (MP mp, str_number t);
1388 void mp_init_map_file (MP mp, int is_troff);
1391 void mp_map_file (MP mp, str_number t) {
1392 char *s = mp_xstrdup(mp,mp_str (mp,t));
1393 mp_process_map_item (mp, s, MAPFILE);
1396 void mp_map_line (MP mp, str_number t) {
1397 char *s = mp_xstrdup(mp,mp_str (mp,t));
1398 mp_process_map_item (mp, s, MAPLINE);
1403 @c void mp_init_map_file (MP mp, int is_troff) {
1405 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1406 mp->ps->mitem->mode = FM_DUPIGNORE;
1407 mp->ps->mitem->type = MAPFILE;
1408 mp->ps->mitem->map_line = NULL;
1409 r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap);
1412 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1415 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1417 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1422 @ @<Dealloc variables@>=
1423 if (mp->ps->mitem!=NULL) {
1424 mp_xfree(mp->ps->mitem->map_line);
1425 mp_xfree(mp->ps->mitem);
1431 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1434 fm = (fm_entry *) pa;
1435 if (!has_pslink (fm))
1436 delete_fm_entry (fm);
1440 static void destroy_fm_entry_ps (void *pa, void *pb) {
1443 fm = (fm_entry *) pa;
1444 if (!has_tfmlink (fm))
1445 delete_fm_entry (fm);
1449 static void destroy_ff_entry (void *pa, void *pb) {
1452 ff = (ff_entry *) pa;
1453 delete_ff_entry (ff);
1457 static void fm_free (MP mp);
1460 static void fm_free (MP mp) {
1461 if (mp->ps->tfm_tree != NULL)
1462 avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1463 if (mp->ps->ps_tree != NULL)
1464 avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1465 if (mp->ps->ff_tree != NULL)
1466 avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1469 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
1470 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
1471 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
1472 one or more spaces, a PostScript name, and possibly a space and some other junk.
1473 This routine reads the table, updates |font_ps_name| entries starting after
1474 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
1476 @d ps_tab_name "psfonts.map" /* locates font name translation table */
1479 static void mp_read_psname_table (MP mp) ;
1482 static void mp_read_psname_table (MP mp) {
1484 if (mp->ps->mitem == NULL) {
1485 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1486 mp->ps->mitem->mode = FM_DUPIGNORE;
1487 mp->ps->mitem->type = MAPFILE;
1488 mp->ps->mitem->map_line = NULL;
1490 mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name);
1492 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
1493 if (mp_has_fm_entry(mp, k, NULL)) {
1494 mp->font_ps_name[k] = mp_fm_font_name(mp,k);
1497 mp->last_ps_fnum=mp->last_fnum;
1501 @ The traditional function is a lot shorter now.
1505 @* \[44c] Helper functions for Type1 fonts.
1508 typedef char char_entry;
1509 typedef unsigned char Byte;
1513 char_entry *char_ptr, *char_array;
1515 char *job_id_string;
1517 @ @<Set initial...@>=
1518 mp->ps->char_array = NULL;
1519 mp->ps->job_id_string = NULL;
1522 @d SMALL_ARRAY_SIZE 256
1526 void mp_set_job_id (MP mp) {
1527 char *name_string, *format_string, *s;
1529 if (mp->ps->job_id_string != NULL)
1531 if ( mp->job_name==NULL )
1532 mp->job_name = mp_xstrdup(mp,"mpout");
1533 name_string = mp_xstrdup (mp,mp->job_name);
1534 format_string = mp_xstrdup (mp,mp->mem_ident);
1535 slen = SMALL_BUF_SIZE +
1536 strlen (name_string) +
1537 strlen (format_string);
1538 s = mp_xmalloc (mp,slen, sizeof (char));
1539 @= /*@@-bufferoverflowhigh@@*/ @>
1540 sprintf (s,"%.4u/%.2u/%.2u %.2u:%.2u %s %s",
1541 ((unsigned)mp->internal[mp_year]>>16),
1542 ((unsigned)mp->internal[mp_month]>>16),
1543 ((unsigned)mp->internal[mp_day]>>16),
1544 ((unsigned)mp->internal[mp_time]>>16) / 60,
1545 ((unsigned)mp->internal[mp_time]>>16) % 60,
1546 name_string, format_string);
1547 @= /*@@=bufferoverflowhigh@@*/ @>
1548 mp->ps->job_id_string = mp_xstrdup (mp,s);
1550 mp_xfree (name_string);
1551 mp_xfree (format_string);
1553 static void fnstr_append (MP mp, const char *s) {
1554 size_t l = strlen (s) + 1;
1555 alloc_array (char, l, SMALL_ARRAY_SIZE);
1556 strcat (mp->ps->char_ptr, s);
1557 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1560 @ @<Exported function headers@>=
1561 void mp_set_job_id (MP mp) ;
1563 @ @<Dealloc variables@>=
1564 mp_xfree(mp->ps->job_id_string);
1566 @ this is not really a true crc32, but it should be just enough to keep
1567 subsets prefixes somewhat disjunct
1570 static unsigned long crc32 (unsigned long oldcrc, const Byte *buf, size_t len) {
1571 unsigned long ret = 0;
1574 ret = (unsigned long)((23<<24)+(45<<16)+(67<<8)+89);
1577 ret = (ret<<2)+buf[i];
1580 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1581 integer b; /* |char_base[f]| */
1583 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1589 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, font_number tex_font)
1595 if (mp->ps->job_id_string ==NULL)
1596 mp_fatal_error(mp, "no job id!");
1597 l = strlen (mp->ps->job_id_string) + 1;
1599 alloc_array (char, l, SMALL_ARRAY_SIZE);
1600 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1601 mp->ps->char_ptr = strend (mp->ps->char_array);
1602 if (fm_cur->tfm_name != NULL) {
1603 fnstr_append (mp," TFM name: ");
1604 fnstr_append (mp,fm_cur->tfm_name);
1606 fnstr_append (mp," PS name: ");
1607 if (fm_cur->ps_name != NULL)
1608 fnstr_append (mp,fm_cur->ps_name);
1609 fnstr_append (mp," Encoding: ");
1610 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1611 fnstr_append (mp,(fm_cur->encoding)->file_name);
1613 fnstr_append (mp,"built-in");
1614 fnstr_append (mp," CharSet: ");
1615 for (i = 0; i < 256; i++)
1616 if (mp_char_marked (mp,tex_font, (eight_bits)i) && glyph_names[i] != notdef) {
1617 if (glyph_names[i]!=NULL) {
1618 fnstr_append (mp,"/");
1619 fnstr_append (mp,glyph_names[i]);
1622 if (fm_cur->charset != NULL) {
1623 fnstr_append (mp," Extra CharSet: ");
1624 fnstr_append (mp, fm_cur->charset);
1626 crc = crc32 (0L, Z_NULL, 0);
1627 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1628 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1629 * there are 26 uppercase chars ==> each char represents a number in range
1630 * |0..25|. The maximal number that can be represented by the tag is
1631 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1632 * of the CRC must be dropped out.
1634 for (i = 0; i < 6; i++) {
1635 tag[i] = 'A' + crc % 26;
1639 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1645 @d external_enc() (fm_cur->encoding)->glyph_names
1646 @d is_used_char(c) mp_char_marked (mp, tex_font, (eight_bits)c)
1647 @d end_last_eexec_line()
1648 mp->ps->hexline_length = HEXLINE_WIDTH;
1650 mp->ps->t1_eexec_encrypt = false
1651 @d t1_log(s) mp_print(mp,(char *)s)
1652 @d t1_putchar(c) wps_chr(c)
1653 @d embed_all_glyphs(tex_font) false
1655 @d extra_charset() mp->ps->dvips_extra_charset
1656 @d update_subset_tag()
1657 @d fixedcontent true
1660 #define PRINTF_BUF_SIZE 1024
1661 char *dvips_extra_charset;
1663 unsigned char *grid;
1664 char *ext_glyph_names[256];
1665 char print_buf[PRINTF_BUF_SIZE];
1666 int t1_byte_waiting;
1668 @ @<Set initial ...@>=
1669 mp->ps->dvips_extra_charset=NULL;
1670 mp->ps->t1_byte_waiting=0;
1673 @d t1_ungetchar(A) mp->ps->t1_byte_waiting=A
1674 @d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file)
1675 @d t1_close() (mp->close_file)(mp,mp->ps->t1_file)
1676 @d valid_code(c) (c >= 0 && c < 256)
1679 static int t1_getchar (MP mp) {
1682 void *byte_ptr = &abyte;
1683 if (mp->ps->t1_byte_waiting) {
1684 abyte = mp->ps->t1_byte_waiting;
1685 mp->ps->t1_byte_waiting = 0;
1687 (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
1692 @ @<Static variables in the outer block@>=
1693 static const char *standard_glyph_names[256] =
1694 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1695 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1696 notdef, notdef, notdef, notdef, notdef, notdef,
1697 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1698 "space", "exclam", "quotedbl", "numbersign",
1699 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1700 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1701 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1702 "eight", "nine", "colon", "semicolon", "less",
1703 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1704 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1705 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1706 "backslash", "bracketright", "asciicircum", "underscore",
1707 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1708 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1709 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1710 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1711 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1712 notdef, notdef, notdef, notdef, notdef, notdef,
1713 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1714 notdef, notdef, notdef, "exclamdown", "cent",
1715 "sterling", "fraction", "yen", "florin", "section", "currency",
1716 "quotesingle", "quotedblleft", "guillemotleft",
1717 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1718 "dagger", "daggerdbl", "periodcentered", notdef,
1719 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1720 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1721 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1722 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1723 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1724 notdef, notdef, notdef, notdef, notdef, notdef,
1725 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1726 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1727 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1728 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1729 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1730 "germandbls", notdef, notdef, notdef, notdef };
1731 static const char charstringname[] = "/CharStrings";
1734 char **t1_glyph_names;
1735 char *t1_builtin_glyph_names[256];
1736 char charsetstr[0x4000];
1737 boolean read_encoding_only;
1741 #define T1_BUF_SIZE 0x10
1745 #define CS_VMOVETO 4
1746 #define CS_RLINETO 5
1747 #define CS_HLINETO 6
1748 #define CS_VLINETO 7
1749 #define CS_RRCURVETO 8
1750 #define CS_CLOSEPATH 9
1751 #define CS_CALLSUBR 10
1752 #define CS_RETURN 11
1753 #define CS_ESCAPE 12
1755 #define CS_ENDCHAR 14
1756 #define CS_RMOVETO 21
1757 #define CS_HMOVETO 22
1758 #define CS_VHCURVETO 30
1759 #define CS_HVCURVETO 31
1760 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1762 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1763 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1764 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1765 #define CS_SEAC CS_1BYTE_MAX + 6
1766 #define CS_SBW CS_1BYTE_MAX + 7
1767 #define CS_DIV CS_1BYTE_MAX + 12
1768 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1769 #define CS_POP CS_1BYTE_MAX + 17
1770 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1771 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1772 #define CS_MAX CS_2BYTE_MAX
1775 typedef unsigned char byte;
1777 byte nargs; /* number of arguments */
1778 boolean bottom; /* take arguments from bottom of stack? */
1779 boolean clear; /* clear stack? */
1781 } cc_entry; /* CharString Command */
1783 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1785 unsigned short len; /* length of the whole string */
1786 unsigned short cslen; /* length of the encoded part of the string */
1796 unsigned short t1_dr, t1_er;
1797 unsigned short t1_cslen;
1801 typedef char t1_line_entry;
1802 typedef char t1_buf_entry;
1805 t1_line_entry *t1_line_ptr, *t1_line_array;
1806 size_t t1_line_limit;
1807 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1808 size_t t1_buf_limit;
1810 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1811 char *cs_dict_start, *cs_dict_end;
1812 int cs_count, cs_size, cs_size_pos;
1814 char *subr_array_start, *subr_array_end;
1815 int subr_max, subr_size, subr_size_pos;
1817 @ @<Set initial...@>=
1818 mp->ps->t1_line_array = NULL;
1819 mp->ps->t1_buf_array = NULL;
1822 This list contains the begin/end tokens commonly used in the
1823 /Subrs array of a Type 1 font.
1825 @<Static variables in the outer block@>=
1826 static const char *cs_token_pairs_list[][2] = {
1829 {" RD", "noaccess put"},
1830 {" -|", "noaccess put"},
1835 const char **cs_token_pair;
1836 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1837 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1838 int t1_block_length;
1846 @<Set initial ...@>=
1847 mp->ps->hexline_length = 0;
1850 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1851 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1852 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1853 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1854 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1855 @d t1_subrs() t1_prefix("/Subrs")
1856 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1857 @d t1_cleartomark() t1_prefix("cleartomark")
1860 static void end_hexline (MP mp) {
1861 if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
1863 mp->ps->hexline_length = 0;
1866 static void t1_check_pfa (MP mp) {
1867 const int c = t1_getchar (mp);
1868 mp->ps->t1_pfa = (c != 128) ? true : false;
1871 static int t1_getbyte (MP mp)
1873 int c = t1_getchar (mp);
1876 if (mp->ps->t1_block_length == 0) {
1878 mp_fatal_error (mp, "invalid marker");
1879 c = t1_getchar (mp);
1882 (void)t1_getchar (mp);
1885 mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
1886 mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 8;
1887 mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 16;
1888 mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 24;
1889 c = t1_getchar (mp);
1891 mp->ps->t1_block_length--;
1894 static int hexval (int c) {
1895 if (c >= 'A' && c <= 'F')
1896 return c - 'A' + 10;
1897 else if (c >= 'a' && c <= 'f')
1898 return c - 'a' + 10;
1899 else if (c >= '0' && c <= '9')
1904 static byte edecrypt (MP mp, byte cipher) {
1906 if (mp->ps->t1_pfa) {
1907 while (cipher == 10 || cipher == 13)
1908 cipher = (byte)t1_getbyte (mp);
1909 mp->ps->last_hexbyte = cipher = (byte)(((byte)hexval (cipher) << 4) +
1910 hexval (t1_getbyte (mp)));
1912 plain = (cipher ^ (mp->ps->t1_dr >> 8));
1913 mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * t1_c1 + t1_c2;
1916 static byte cdecrypt (byte cipher, unsigned short *cr)
1918 const byte plain = (cipher ^ (*cr >> 8));
1919 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1922 static byte eencrypt (MP mp, byte plain)
1924 const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1925 mp->ps->t1_er = (cipher + mp->ps->t1_er) * t1_c1 + t1_c2;
1929 static byte cencrypt (byte plain, unsigned short *cr)
1931 const byte cipher = (plain ^ (*cr >> 8));
1932 *cr = (cipher + *cr) * t1_c1 + t1_c2;
1936 static char *eol (char *s) {
1937 char *p = strend (s);
1938 if (p!=NULL && p - s > 1 && p[-1] != 10) {
1944 static float t1_scan_num (MP mp, char *p, char **r)
1949 if (sscanf (p, "%g", &f) != 1) {
1950 remove_eol (p, mp->ps->t1_line_array);
1951 mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1952 mp_fatal_error(mp,s);
1955 for (; mp_isdigit (*p) || *p == '.' ||
1956 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1962 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1965 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1968 while (s1 >= begin_buf && s2 >= s) {
1977 @d alloc_array(T, n, s) do {
1978 if (mp->ps->T##_array == NULL) {
1979 mp->ps->T##_limit = (s);
1980 if ((size_t)(n) > mp->ps->T##_limit)
1981 mp->ps->T##_limit = (size_t)(n);
1982 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
1983 mp->ps->T##_ptr = mp->ps->T##_array;
1985 else if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
1986 size_t last_ptr_index;
1987 last_ptr_index = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array);
1988 mp->ps->T##_limit *= 2;
1989 if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
1990 mp->ps->T##_limit = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n));
1991 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
1992 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
1996 @d out_eexec_char(A) t1_outhex(mp,(A))
1999 static void t1_outhex (MP mp, byte b)
2001 static const char *hexdigits = "0123456789ABCDEF";
2002 t1_putchar (hexdigits[b / 16]);
2003 t1_putchar (hexdigits[b % 16]);
2004 mp->ps->hexline_length += 2;
2007 static void t1_getline (MP mp) {
2008 int c, l, eexec_scan;
2010 static const char eexec_str[] = "currentfile eexec";
2011 static int eexec_len = 17; /* |strlen(eexec_str)| */
2014 mp_fatal_error (mp,"unexpected end of file");
2015 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2016 alloc_array (t1_line, 1, T1_BUF_SIZE);
2017 mp->ps->t1_cslen = 0;
2019 c = t1_getbyte (mp);
2022 while (!t1_eof ()) {
2023 if (mp->ps->t1_in_eexec == 1)
2024 c = edecrypt (mp,(byte)c);
2025 alloc_array (t1_line, 1, T1_BUF_SIZE);
2026 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2027 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
2028 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
2033 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
2035 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
2036 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
2037 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
2038 p = mp->ps->t1_line_ptr - 5;
2041 l = (int)t1_scan_num (mp, p + 1, 0);
2042 mp->ps->t1_cslen = (unsigned short)l;
2043 mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2044 /* |mp->ps->cs_start| is an index now */
2045 alloc_array (t1_line, l, T1_BUF_SIZE);
2047 *mp->ps->t1_line_ptr = (t1_line_entry)edecrypt (mp,(byte)t1_getbyte (mp));
2048 mp->ps->t1_line_ptr++;
2051 c = t1_getbyte (mp);
2053 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
2054 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
2055 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
2057 if (eexec_scan == eexec_len)
2058 mp->ps->t1_in_eexec = 1;
2060 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
2061 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
2062 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
2065 static void t1_putline (MP mp)
2067 char *p = mp->ps->t1_line_array;
2068 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2070 if (mp->ps->t1_eexec_encrypt) {
2071 while (p < mp->ps->t1_line_ptr)
2072 out_eexec_char (eencrypt (mp,(byte)*p++));
2074 while (p < mp->ps->t1_line_ptr)
2079 static void t1_puts (MP mp, const char *s)
2081 if (s != mp->ps->t1_line_array)
2082 strcpy (mp->ps->t1_line_array, s);
2083 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2087 static void t1_init_params (MP mp, const char *open_name_prefix,
2088 char *cur_file_name) {
2089 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2090 t1_log (open_name_prefix);
2091 t1_log (cur_file_name);
2093 mp->ps->t1_lenIV = 4;
2094 mp->ps->t1_dr = 55665;
2095 mp->ps->t1_er = 55665;
2096 mp->ps->t1_in_eexec = 0;
2097 mp->ps->t1_cs = false;
2098 mp->ps->t1_scan = true;
2099 mp->ps->t1_synthetic = false;
2100 mp->ps->t1_eexec_encrypt = false;
2101 mp->ps->t1_block_length = 0;
2104 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2105 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2106 t1_log (close_name_suffix);
2111 static void t1_check_block_len (MP mp, boolean decrypt) {
2114 if (mp->ps->t1_block_length == 0)
2116 c = t1_getbyte (mp);
2118 c = edecrypt (mp,(byte)c);
2119 l = mp->ps->t1_block_length;
2120 if (!(l == 0 && (c == 10 || c == 13))) {
2121 mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2124 (void)t1_getbyte (mp);
2127 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2129 if (!mp->ps->t1_pfa)
2130 t1_check_block_len (mp, false);
2131 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2132 (void)edecrypt (mp, (byte)t1_getbyte (mp));
2133 *mp->ps->t1_line_ptr++ = 0;
2135 mp->ps->t1_eexec_encrypt = true;
2136 if (!mp->ps->read_encoding_only)
2137 if (is_included (fm_cur))
2138 t1_putline (mp); /* to put the first four bytes */
2140 static void t1_stop_eexec (MP mp) {
2142 end_last_eexec_line ();
2143 if (!mp->ps->t1_pfa)
2144 t1_check_block_len (mp,true);
2146 c = edecrypt (mp, (byte)t1_getbyte (mp));
2147 if (!(c == 10 || c == 13)) {
2148 if (mp->ps->last_hexbyte == 0)
2151 mp_warn (mp,"unexpected data after eexec");
2154 mp->ps->t1_cs = false;
2155 mp->ps->t1_in_eexec = 2;
2157 static void t1_modify_fm (MP mp) {
2158 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2161 static void t1_modify_italic (MP mp) {
2162 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2167 const char *pdfname;
2177 static key_entry font_keys[FONT_KEYS_NUM] = {
2178 {"Ascent", "Ascender", 0, false},
2179 {"CapHeight", "CapHeight", 0, false},
2180 {"Descent", "Descender", 0, false},
2181 {"FontName", "FontName", 0, false},
2182 {"ItalicAngle", "ItalicAngle", 0, false},
2183 {"StemV", "StdVW", 0, false},
2184 {"XHeight", "XHeight", 0, false},
2185 {"FontBBox", "FontBBox", 0, false},
2197 @d ITALIC_ANGLE_CODE 4
2203 @d FONTBBOX4_CODE 10
2204 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2207 static void t1_scan_keys (MP mp, font_number tex_font,fm_entry *fm_cur) {
2211 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2212 if (t1_prefix ("/FontMatrix")) {
2216 if (t1_prefix ("/ItalicAngle")) {
2217 t1_modify_italic (mp);
2221 if (t1_prefix ("/FontType")) {
2222 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2223 if ((i = (int)t1_scan_num (mp,p, 0)) != 1) {
2225 mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2226 mp_fatal_error(mp,s);
2230 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2231 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2233 if (key - font_keys == MAX_KEY_CODE)
2236 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2238 if ((k = key - font_keys) == FONTNAME_CODE) {
2241 remove_eol (p, mp->ps->t1_line_array);
2242 mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2243 mp_fatal_error(mp,s);
2245 r = ++p; /* skip the slash */
2246 if (is_included (fm_cur)) {
2247 /* save the fontname */
2248 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2249 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2250 mp->ps->fontname_buf[i]=0;
2252 if(is_subsetted (fm_cur)) {
2253 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2254 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2256 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2258 alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
2260 strncpy (r, fm_cur->subset_tag , 6);
2262 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2263 mp->ps->t1_line_ptr = eol (r);
2265 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2267 mp->ps->t1_line_ptr = eol (r);
2272 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2273 && (*p == '[' || *p == '{'))
2275 if (k == FONTBBOX1_CODE) {
2276 for (i = 0; i < 4; i++) {
2277 key[i].value = t1_scan_num (mp, p, &r);
2282 key->value = t1_scan_num (mp, p, 0);
2284 static void t1_scan_param (MP mp, font_number tex_font,fm_entry *fm_cur)
2286 static const char *lenIV = "/lenIV";
2287 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2289 if (t1_prefix (lenIV)) {
2290 mp->ps->t1_lenIV = (short int)t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2293 t1_scan_keys (mp, tex_font,fm_cur);
2295 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2296 if (glyph_names[b] != notdef) {
2297 mp_xfree (glyph_names[b]);
2298 glyph_names[b] = (char *) notdef;
2300 if (glyph_names[a] != notdef) {
2301 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2304 static void t1_builtin_enc (MP mp) {
2305 int i, a, b, c, counter = 0;
2308 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2310 if (t1_suffix ("def")) { /* predefined encoding */
2311 (void)sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2312 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2313 for (i = 0; i < 256; i++)
2314 if (standard_glyph_names[i] == notdef)
2315 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2317 mp->ps->t1_builtin_glyph_names[i] =
2318 mp_xstrdup (mp,standard_glyph_names[i]);
2319 mp->ps->t1_encoding = ENC_STANDARD;
2322 mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2323 mp->ps->t1_buf_array);
2324 mp_fatal_error(mp,s);
2328 mp->ps->t1_encoding = ENC_BUILTIN;
2330 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2331 * not a predefined encoding
2333 * We have two possible forms of Encoding vector. The first case is
2335 * /Encoding [/a /b /c...] readonly def
2337 * and the second case can look like
2339 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2345 for (i = 0; i < 256; i++)
2346 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2347 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2348 r = strchr (mp->ps->t1_line_array, '[') + 1;
2352 for (p = mp->ps->t1_buf_array, r++;
2353 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2357 if (counter > 255) {
2359 (mp, "encoding vector contains more than 256 names");
2361 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2362 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2365 if (*r != 10 && *r != '%') {
2366 if (str_prefix (r, "] def")
2367 || str_prefix (r, "] readonly def"))
2371 remove_eol (r, mp->ps->t1_line_array);
2372 mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2373 mp->ps->t1_line_array);
2374 mp_fatal_error(mp,s);
2378 r = mp->ps->t1_line_array;
2380 } else { /* the second case */
2381 p = strchr (mp->ps->t1_line_array, 10);
2385 p = mp->ps->t1_line_array;
2388 check for `dup <index> <glyph> put'
2390 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2391 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2392 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2393 mp->ps->t1_builtin_glyph_names[i] =
2394 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2395 p = strstr (p, " put") + strlen (" put");
2399 check for `dup dup <to> exch <from> get put'
2401 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2402 && valid_code (a) && valid_code (b)) {
2403 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2404 p = strstr (p, " get put") + strlen (" get put");
2408 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2411 (p, "dup dup %i %i getinterval %i exch putinterval",
2412 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2413 && valid_code (c)) {
2414 for (i = 0; i < c; i++)
2415 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2416 p = strstr (p, " putinterval") + strlen (" putinterval");
2420 check for `def' or `readonly def'
2422 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2423 && strcmp (p, "def\n") == 0)
2426 skip an unrecognizable word
2429 while (*p != ' ' && *p != 10)
2437 static void t1_check_end (MP mp) {
2441 if (t1_prefix ("{restore}"))
2447 char *ff_name; /* base name of font file */
2448 char *ff_path; /* full path to font file */
2452 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2454 ff = check_ff_exist (mp, fm_cur);
2455 if (ff->ff_path != NULL) {
2456 mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font);
2458 mp_warn (mp, "cannot open Type 1 font file for reading");
2461 t1_init_params (mp,open_name_prefix,fm_cur->ff_name);
2462 mp->ps->fontfile_found = true;
2466 static void t1_scan_only (MP mp, font_number tex_font, fm_entry *fm_cur) {
2469 t1_scan_param (mp,tex_font, fm_cur);
2471 while (mp->ps->t1_in_eexec == 0);
2472 t1_start_eexec (mp,fm_cur);
2475 t1_scan_param (mp,tex_font, fm_cur);
2477 while (!(t1_charstrings () || t1_subrs ()));
2480 static void t1_include (MP mp, font_number tex_font, fm_entry *fm_cur) {
2483 t1_scan_param (mp,tex_font, fm_cur);
2486 while (mp->ps->t1_in_eexec == 0);
2487 t1_start_eexec (mp,fm_cur);
2490 t1_scan_param (mp,tex_font, fm_cur);
2493 while (!(t1_charstrings () || t1_subrs ()));
2494 mp->ps->t1_cs = true;
2499 while (!t1_end_eexec ());
2501 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2506 while (!t1_cleartomark ());
2507 t1_check_end (mp); /* write "{restore}if" if found */
2512 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2514 mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2515 mp_fatal_error(mp,s);
2519 static const char **check_cs_token_pair (MP mp) {
2520 const char **p = (const char **) cs_token_pairs_list;
2521 for (; p[0] != NULL; ++p)
2522 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2527 static void cs_store (MP mp, boolean is_subr) {
2531 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2532 *mp->ps->t1_buf_ptr++ = *p++);
2533 *mp->ps->t1_buf_ptr = 0;
2535 subr = (int)t1_scan_num (mp, p + 1, 0);
2537 ptr = mp->ps->subr_tab + subr;
2539 ptr = mp->ps->cs_ptr++;
2540 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2542 mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2543 mp_fatal_error(mp,s);
2545 if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
2546 ptr->glyph_name = (char *) notdef;
2548 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2550 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2551 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2552 (size_t) (mp->ps->t1_cslen + 4));
2553 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2554 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2555 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2556 *mp->ps->t1_buf_ptr++ = 10;
2557 if (is_subr && mp->ps->cs_token_pair == NULL)
2558 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2559 ptr->len = (unsigned short)(mp->ps->t1_buf_ptr - mp->ps->t1_buf_array);
2560 ptr->cslen = mp->ps->t1_cslen;
2561 ptr->data = mp_xmalloc (mp, (size_t)ptr->len , sizeof (byte));
2562 memcpy (ptr->data, mp->ps->t1_buf_array, (size_t)ptr->len);
2566 #define store_subr(mp) cs_store(mp,true)
2567 #define store_cs(mp) cs_store(mp,false)
2569 #define CC_STACK_SIZE 24
2571 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2572 static cc_entry cc_tab[CS_MAX];
2573 static boolean is_cc_init = false;
2577 if (stack_ptr - cc_stack < (N)) \
2581 #define stack_error(N) { \
2583 mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2584 (int) N, (int)(stack_ptr - cc_stack)); \
2590 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2592 #define cc_push(V) *stack_ptr++ = V
2593 #define cc_clear() stack_ptr = cc_stack
2595 #define set_cc(N, B, A, C) \
2596 cc_tab[N].nargs = A; \
2597 cc_tab[N].bottom = B; \
2598 cc_tab[N].clear = C; \
2599 cc_tab[N].valid = true
2601 static void cc_init (void) {
2605 for (i = 0; i < CS_MAX; i++)
2606 cc_tab[i].valid = false;
2607 set_cc (CS_HSTEM, true, 2, true);
2608 set_cc (CS_VSTEM, true, 2, true);
2609 set_cc (CS_VMOVETO, true, 1, true);
2610 set_cc (CS_RLINETO, true, 2, true);
2611 set_cc (CS_HLINETO, true, 1, true);
2612 set_cc (CS_VLINETO, true, 1, true);
2613 set_cc (CS_RRCURVETO, true, 6, true);
2614 set_cc (CS_CLOSEPATH, false, 0, true);
2615 set_cc (CS_CALLSUBR, false, 1, false);
2616 set_cc (CS_RETURN, false, 0, false);
2618 |set_cc(CS_ESCAPE, false, 0, false);|
2620 set_cc (CS_HSBW, true, 2, true);
2621 set_cc (CS_ENDCHAR, false, 0, true);
2622 set_cc (CS_RMOVETO, true, 2, true);
2623 set_cc (CS_HMOVETO, true, 1, true);
2624 set_cc (CS_VHCURVETO, true, 4, true);
2625 set_cc (CS_HVCURVETO, true, 4, true);
2626 set_cc (CS_DOTSECTION, false, 0, true);
2627 set_cc (CS_VSTEM3, true, 6, true);
2628 set_cc (CS_HSTEM3, true, 6, true);
2629 set_cc (CS_SEAC, true, 5, true);
2630 set_cc (CS_SBW, true, 4, true);
2631 set_cc (CS_DIV, false, 2, false);
2632 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2633 set_cc (CS_POP, false, 0, false);
2634 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2640 @d cs_getchar(mp) cdecrypt(*data++, &cr)
2642 @d mark_subr(mp,n) cs_mark(mp,0, n)
2643 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2644 @d SMALL_BUF_SIZE 256
2647 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2648 char buf[SMALL_BUF_SIZE];
2651 va_start (args, fmt);
2652 @= /*@@-bufferoverflowhigh@@*/ @>
2653 (void)vsprintf (buf, fmt, args);
2654 @= /*@@=bufferoverflowhigh@@*/ @>
2656 if (cs_name == NULL) {
2657 mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2659 mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2664 static void cs_mark (MP mp, const char *cs_name, int subr)
2670 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2674 if (cs_name == NULL) {
2676 ptr = mp->ps->subr_tab + subr;
2680 if (mp->ps->cs_notdef != NULL &&
2681 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2682 ptr = mp->ps->cs_notdef;
2684 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2685 if (strcmp (ptr->glyph_name, cs_name) == 0)
2687 if (ptr == mp->ps->cs_ptr) {
2689 mp_snprintf (s,128,"glyph `%s' undefined", cs_name);
2693 if (ptr->glyph_name == notdef)
2694 mp->ps->cs_notdef = ptr;
2697 /* only marked CharString entries and invalid entries can be skipped;
2698 valid marked subrs must be parsed to keep the stack in sync */
2699 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2701 ptr->is_used = true;
2703 cs_len = (int)ptr->cslen;
2704 data = ptr->data + 4;
2705 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2706 (void)cs_getchar (mp);
2707 while (cs_len > 0) {
2709 b = cs_getchar (mp);
2713 else if (b <= 250) {
2715 a = (int)((unsigned)(b - 247) << 8) + 108 + cs_getchar (mp);
2716 } else if (b <= 254) {
2718 a = -(int)((unsigned)(b - 251) << 8) - 108 - cs_getchar (mp);
2721 a = (cs_getchar (mp) & 0xff) << 24;
2722 a |= (cs_getchar (mp) & 0xff) << 16;
2723 a |= (cs_getchar (mp) & 0xff) << 8;
2724 a |= (cs_getchar (mp) & 0xff) << 0;
2725 if (sizeof (integer) > 4 && (a & 0x80000000))
2730 if (b == CS_ESCAPE) {
2731 b = cs_getchar (mp) + CS_1BYTE_MAX;
2735 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2741 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2745 if (stack_ptr - cc_stack < cc->nargs)
2746 cs_warn (mp,cs_name, subr,
2747 "less arguments on stack (%i) than required (%i)",
2748 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2749 else if (stack_ptr - cc_stack > cc->nargs)
2750 cs_warn (mp,cs_name, subr,
2751 "more arguments on stack (%i) than required (%i)",
2752 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2754 switch (cc - cc_tab) {
2759 if (!mp->ps->subr_tab[a1].valid) {
2760 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2768 case CS_CALLOTHERSUBR:
2769 if (cc_get (-1) == 3)
2770 lastargOtherSubr3 = cc_get (-3);
2771 a1 = cc_get (-2) + 2;
2775 cc_push (lastargOtherSubr3);
2776 /* the only case when we care about the value being pushed onto
2777 stack is when POP follows CALLOTHERSUBR (changing hints by
2785 mark_cs (mp,standard_glyph_names[a1]);
2786 mark_cs (mp,standard_glyph_names[a2]);
2795 cs_error: /* an error occured during parsing */
2798 ptr->is_used = false;
2801 static void t1_subset_ascii_part (MP mp, font_number tex_font, fm_entry *fm_cur)
2805 while (!t1_prefix ("/Encoding")) {
2806 t1_scan_param (mp,tex_font, fm_cur);
2810 t1_builtin_enc (mp);
2811 if (is_reencoded (fm_cur))
2812 mp->ps->t1_glyph_names = external_enc ();
2814 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2816 |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2817 make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2818 update_subset_tag ();
2821 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2822 t1_puts (mp,"/Encoding StandardEncoding def\n");
2825 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2826 for (i = 0, j = 0; i < 256; i++) {
2827 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2829 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
2830 "dup %i /%s put\n", (int) t1_char (i),
2831 mp->ps->t1_glyph_names[i]);
2832 t1_puts(mp,mp->ps->t1_line_array);
2835 /* We didn't mark anything for the Encoding array. */
2836 /* We add "dup 0 /.notdef put" for compatibility */
2837 /* with Acrobat 5.0. */
2839 t1_puts (mp,"dup 0 /.notdef put\n");
2840 t1_puts (mp,"readonly def\n");
2844 t1_scan_param (mp,tex_font, fm_cur);
2845 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
2848 while (mp->ps->t1_in_eexec == 0);
2851 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
2852 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
2854 static void cs_init (MP mp) {
2855 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2856 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2857 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2858 mp->ps->cs_token_pair = NULL;
2859 mp->ps->subr_tab = NULL;
2860 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2861 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2864 static void init_cs_entry ( cs_entry * cs) {
2866 cs->glyph_name = NULL;
2869 cs->is_used = false;
2873 static void t1_mark_glyphs (MP mp, font_number tex_font);
2875 static void t1_read_subrs (MP mp, font_number tex_font, fm_entry *fm_cur)
2880 while (!(t1_charstrings () || t1_subrs ())) {
2881 t1_scan_param (mp,tex_font, fm_cur);
2886 mp->ps->t1_cs = true;
2887 mp->ps->t1_scan = false;
2890 mp->ps->subr_size_pos = (int)(strlen ("/Subrs") + 1);
2891 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2892 mp->ps->subr_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2893 if (mp->ps->subr_size == 0) {
2894 while (!t1_charstrings ())
2898 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
2899 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,(size_t)mp->ps->subr_size, sizeof (cs_entry));
2900 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2901 init_cs_entry (ptr);
2902 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2904 while (mp->ps->t1_cslen) {
2908 /* mark the first four entries without parsing */
2909 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2910 mp->ps->subr_tab[i].is_used = true;
2911 /* the end of the Subrs array might have more than one line so we need to
2912 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2913 the Subrs array followed by the CharStrings dict immediately (synthetic
2914 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2915 we will treat the font as synthetic and ignore everything until next
2918 #define POST_SUBRS_SCAN 5
2920 *mp->ps->t1_buf_array = 0;
2921 for (i = 0; i < POST_SUBRS_SCAN; i++) {
2922 if (t1_charstrings ())
2924 s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2925 alloc_array (t1_buf, s, T1_BUF_SIZE);
2926 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2929 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2930 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2931 suppose synthetic font */
2932 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2934 mp_xfree (ptr->data);
2935 mp_xfree (mp->ps->subr_tab);
2936 mp_xfree (mp->ps->subr_array_start);
2937 mp_xfree (mp->ps->subr_array_end);
2939 mp->ps->t1_cs = false;
2940 mp->ps->t1_synthetic = true;
2941 while (!(t1_charstrings () || t1_subrs ()))
2948 static void t1_flush_cs (MP mp, boolean is_subr)
2951 byte *r, *return_cs = NULL;
2952 cs_entry *tab, *end_tab, *ptr;
2953 char *start_line, *line_end;
2954 int count, size_pos;
2955 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2957 start_line = mp->ps->subr_array_start;
2958 line_end = mp->ps->subr_array_end;
2959 size_pos = mp->ps->subr_size_pos;
2960 tab = mp->ps->subr_tab;
2961 count = mp->ps->subr_max + 1;
2962 end_tab = mp->ps->subr_tab + count;
2964 start_line = mp->ps->cs_dict_start;
2965 line_end = mp->ps->cs_dict_end;
2966 size_pos = mp->ps->cs_size_pos;
2967 tab = mp->ps->cs_tab;
2968 end_tab = mp->ps->cs_ptr;
2969 count = mp->ps->cs_count;
2971 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2972 for (p = start_line; p - start_line < size_pos;)
2973 *mp->ps->t1_line_ptr++ = *p++;
2974 while (mp_isdigit (*p))
2976 mp_snprintf (mp->ps->t1_line_ptr, (int)mp->ps->t1_line_limit, "%u", (unsigned)count);
2977 strcat (mp->ps->t1_line_ptr, p);
2978 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2981 /* create |return_cs| to replace unsused subr's */
2985 return_cs = mp_xmalloc (mp, (size_t)(mp->ps->t1_lenIV + 1) , sizeof(byte));
2986 if ( mp->ps->t1_lenIV > 0) {
2987 for (cs_len = 0, r = return_cs;
2988 cs_len<(unsigned short)mp->ps->t1_lenIV; cs_len++, r++)
2989 *r = cencrypt (0x00, &cr);
2990 *r = cencrypt (CS_RETURN, &cr);
2992 *return_cs = CS_RETURN;
2997 for (ptr = tab; ptr < end_tab; ptr++) {
3000 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3001 "dup %i %u", (int) (ptr - tab), ptr->cslen);
3003 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3004 "/%s %u", ptr->glyph_name, ptr->cslen);
3005 p = strend (mp->ps->t1_line_array);
3006 memcpy (p, ptr->data, (size_t)ptr->len);
3007 mp->ps->t1_line_ptr = p + ptr->len;
3010 /* replace unsused subr's by |return_cs| */
3012 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3013 "dup %i %u%s ", (int) (ptr - tab),
3014 cs_len, mp->ps->cs_token_pair[0]);
3015 p = strend (mp->ps->t1_line_array);
3016 memcpy (p, return_cs, (size_t)cs_len);
3017 mp->ps->t1_line_ptr = p + cs_len;
3019 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
3020 " %s", mp->ps->cs_token_pair[1]);
3021 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3025 mp_xfree (ptr->data);
3026 if (ptr->glyph_name != notdef)
3027 mp_xfree (ptr->glyph_name);
3029 mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "%s", line_end);
3030 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
3033 mp_xfree (return_cs);
3035 mp_xfree (start_line);
3036 mp_xfree (line_end);
3039 static void t1_mark_glyphs (MP mp, font_number tex_font)
3042 char *charset = extra_charset ();
3045 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
3046 if (mp->ps->cs_tab != NULL)
3047 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3049 ptr->is_used = true;
3050 if (mp->ps->subr_tab != NULL) {
3051 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
3053 ptr->is_used = true;
3054 mp->ps->subr_max = mp->ps->subr_size - 1;
3058 mark_cs (mp,notdef);
3059 for (i = 0; i < 256; i++)
3060 if (is_used_char (i)) {
3061 if (mp->ps->t1_glyph_names[i] == notdef) {
3063 mp_snprintf(S,128, "character %i is mapped to %s", i, notdef);
3066 mark_cs (mp,mp->ps->t1_glyph_names[i]);
3068 if (charset == NULL)
3070 g = s = charset + 1; /* skip the first '/' */
3073 while (*s != '/' && s < r)
3075 *s = 0; /* terminate g by rewriting '/' to 0 */
3080 if (mp->ps->subr_tab != NULL)
3081 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3082 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3084 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3085 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3088 static void t1_subset_charstrings (MP mp, font_number tex_font)
3091 mp->ps->cs_size_pos =
3092 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3093 - mp->ps->t1_line_array + 1;
3094 /* |cs_size_pos| points to the number indicating
3095 dict size after "/CharStrings" */
3096 mp->ps->cs_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3097 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,(size_t)mp->ps->cs_size, sizeof(cs_entry));
3098 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3099 init_cs_entry (ptr);
3100 mp->ps->cs_notdef = NULL;
3101 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3103 while (mp->ps->t1_cslen) {
3107 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3108 t1_mark_glyphs (mp,tex_font);
3109 if (mp->ps->subr_tab != NULL) {
3110 if (mp->ps->cs_token_pair == NULL)
3112 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3115 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3121 static void t1_subset_end (MP mp)
3123 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3124 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3128 while (!t1_end_eexec ())
3129 t1_getline (mp); /* ignore the rest */
3130 t1_putline (mp); /* write "mark currentfile closefile" */
3132 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3137 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3138 while (!t1_cleartomark ()) {
3142 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3143 t1_check_end (mp); /* write "{restore}if" if found */
3147 static int t1_updatefm (MP mp, font_number f, fm_entry *fm)
3150 mp->ps->read_encoding_only = true;
3151 if (!t1_open_fontfile (mp,fm,NULL)) {
3154 t1_scan_only (mp,f, fm);
3155 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3157 while (*p != ' ' && *p != 0)
3161 t1_close_font_file (mp,"");
3166 static void writet1 (MP mp, font_number tex_font, fm_entry *fm_cur) {
3167 unsigned save_selector = mp->selector;
3168 mp_normalize_selector(mp);
3169 mp->ps->read_encoding_only = false;
3170 if (!is_included (fm_cur)) { /* scan parameters from font file */
3171 if (!t1_open_fontfile (mp,fm_cur,"{"))
3173 t1_scan_only (mp,tex_font, fm_cur);
3174 t1_close_font_file (mp,"}");
3177 if (!is_subsetted (fm_cur)) { /* include entire font */
3178 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3180 t1_include (mp,tex_font,fm_cur);
3181 t1_close_font_file (mp,">>");
3184 /* partial downloading */
3185 if (!t1_open_fontfile (mp,fm_cur,"<"))
3187 t1_subset_ascii_part (mp,tex_font,fm_cur);
3188 t1_start_eexec (mp,fm_cur);
3191 t1_read_subrs (mp,tex_font, fm_cur);
3192 t1_subset_charstrings (mp,tex_font);
3194 t1_close_font_file (mp,">");
3195 mp->selector = save_selector;
3199 static void t1_free (MP mp);
3202 static void t1_free (MP mp) {
3203 mp_xfree (mp->ps->t1_line_array);
3204 mp_xfree (mp->ps->t1_buf_array);
3208 @* \[44d] Embedding fonts.
3210 @ The |tfm_num| is officially of type |font_number|, but that
3211 type does not exist yet at this point in the output order.
3215 char *tfm_name; /* TFM file name */
3216 char *ps_name; /* PostScript name */
3217 integer flags; /* font flags */
3218 char *ff_name; /* font file name */
3219 char *subset_tag; /* pseudoUniqueTag for subsetted font */
3220 enc_entry *encoding; /* pointer to corresponding encoding */
3221 unsigned int tfm_num; /* number of the TFM refering this entry */
3222 unsigned short type; /* font type (T1/TTF/...) */
3223 short slant; /* SlantFont */
3224 short extend; /* ExtendFont */
3225 integer ff_objnum; /* FontFile object number */
3226 integer fn_objnum; /* FontName/BaseName object number */
3227 integer fd_objnum; /* FontDescriptor object number */
3228 char *charset; /* string containing used glyphs */
3229 boolean all_glyphs; /* embed all glyphs? */
3230 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
3231 short tfm_avail; /* flags whether a tfm is available */
3232 short pid; /* Pid for truetype fonts */
3233 short eid; /* Eid for truetype fonts */
3239 #define FONTNAME_BUF_SIZE 128
3240 boolean fontfile_found;
3241 boolean is_otf_font;
3242 char fontname_buf[FONTNAME_BUF_SIZE];
3250 @d set_included(fm) ((fm)->type |= F_INCLUDED)
3251 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
3252 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
3253 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
3255 @d is_included(fm) ((fm)->type & F_INCLUDED)
3256 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
3257 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
3258 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
3259 @d is_reencoded(fm) ((fm)->encoding != NULL)
3260 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
3261 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
3263 @d fm_slant(fm) (fm)->slant
3264 @d fm_extend(fm) (fm)->extend
3265 @d fm_fontfile(fm) (fm)->ff_name
3268 static boolean mp_font_is_reencoded (MP mp, font_number f);
3269 static boolean mp_font_is_included (MP mp, font_number f);
3270 static boolean mp_font_is_subsetted (MP mp, font_number f);
3273 boolean mp_font_is_reencoded (MP mp, font_number f) {
3275 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3277 && (fm->ps_name != NULL)
3278 && is_reencoded (fm))
3283 boolean mp_font_is_included (MP mp, font_number f) {
3285 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3287 && (fm->ps_name != NULL && fm->ff_name != NULL)
3288 && is_included (fm))
3293 boolean mp_font_is_subsetted (MP mp, font_number f) {
3295 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
3297 && (fm->ps_name != NULL && fm->ff_name != NULL)
3298 && is_included (fm) && is_subsetted (fm))
3305 static char * mp_fm_encoding_name (MP mp, font_number f);
3306 static char * mp_fm_font_name (MP mp, font_number f);
3307 static char * mp_fm_font_subset_name (MP mp, font_number f);
3310 @c char * mp_fm_encoding_name (MP mp, font_number f) {
3313 if (mp_has_fm_entry (mp, f, &fm)) {
3314 if (fm != NULL && (fm->ps_name != NULL)) {
3315 if (is_reencoded (fm)) {
3317 if (e->enc_name!=NULL)
3318 return mp_xstrdup(mp,e->enc_name);
3324 print_err ("fontmap encoding problems for font ");
3325 mp_print(mp,mp->font_name[f]);
3329 char * mp_fm_font_name (MP mp, font_number f) {
3331 if (mp_has_fm_entry (mp, f,&fm)) {
3332 if (fm != NULL && (fm->ps_name != NULL)) {
3333 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3334 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3335 if (t1_updatefm(mp,f,fm)) {
3336 mp->font_ps_name_fixed[f] = true;
3338 print_err ("font loading problems for font ");
3339 mp_print(mp,mp->font_name[f]);
3343 return mp_xstrdup(mp,fm->ps_name);
3346 print_err ("fontmap name problems for font ");
3347 mp_print(mp,mp->font_name[f]);
3352 static char * mp_fm_font_subset_name (MP mp, font_number f) {
3354 if (mp_has_fm_entry (mp, f, &fm)) {
3355 if (fm != NULL && (fm->ps_name != NULL)) {
3356 if (is_subsetted(fm)) {
3357 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3358 mp_snprintf(s,(int)strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3361 return mp_xstrdup(mp,fm->ps_name);
3365 print_err ("fontmap name problems for font ");
3366 mp_print(mp,mp->font_name[f]);
3372 static integer mp_fm_font_slant (MP mp, font_number f);
3373 static integer mp_fm_font_extend (MP mp, font_number f);
3376 @c static integer mp_fm_font_slant (MP mp, font_number f) {
3378 if (mp_has_fm_entry (mp, f, &fm)) {
3379 if (fm != NULL && (fm->ps_name != NULL)) {
3385 static integer mp_fm_font_extend (MP mp, font_number f) {
3387 if (mp_has_fm_entry (mp, f, &fm)) {
3388 if (fm != NULL && (fm->ps_name != NULL)) {
3396 static boolean mp_do_ps_font (MP mp, font_number f);
3398 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3400 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3403 if (is_truetype(fm_cur) ||
3404 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3407 if (is_included(fm_cur)) {
3408 mp_ps_print_nl(mp,"%%BeginResource: font ");
3409 if (is_subsetted(fm_cur)) {
3410 mp_ps_print(mp, fm_cur->subset_tag);
3411 mp_ps_print_char(mp,'-');
3413 mp_ps_print(mp, fm_cur->ps_name);
3415 writet1 (mp,f,fm_cur);
3416 mp_ps_print_nl(mp,"%%EndResource");
3422 @ Included subset fonts do not need and encoding vector, make
3423 sure we skip that case.
3426 static void mp_list_used_resources (MP mp, int prologues, int procset);
3428 @ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
3429 font_number f; /* fonts used in a text node or as loop counters */
3430 int ff; /* a loop counter */
3431 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3434 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
3436 mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3439 for (f=null_font+1;f<=mp->last_fnum;f++) {
3440 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3441 for (ff=ldf;ff>=null_font;ff--) {
3442 if ( mp_has_font_size(mp,(font_number)ff) )
3443 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3446 if ( mp_font_is_subsetted(mp,f) )
3448 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
3449 (size_t)mp->max_print_line )
3450 mp_ps_print_nl(mp, "%%+ encoding");
3453 mp_ps_print_nl(mp, "%%+ encoding");
3455 mp_ps_print_char(mp, ' ');
3456 mp_ps_print(mp, mp->font_enc_name[f]);
3464 for (f=null_font+1;f<=mp->last_fnum;f++) {
3465 if ( mp_has_font_size(mp,f) ) {
3466 for (ff=ldf;ff>=null_font;ff--) {
3467 if ( mp_has_font_size(mp,(font_number)ff) )
3468 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3471 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
3472 (size_t)mp->max_print_line )
3473 mp_ps_print_nl(mp, "%%+ font");
3476 mp_ps_print_nl(mp, "%%+ font");
3478 mp_ps_print_char(mp, ' ');
3479 if ( (prologues==3)&&
3480 (mp_font_is_subsetted(mp,f)) )
3481 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3483 mp_ps_print(mp, mp->font_ps_name[f]);
3493 static void mp_list_supplied_resources (MP mp, int prologues, int procset);
3495 @ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
3496 font_number f; /* fonts used in a text node or as loop counters */
3497 int ff; /* a loop counter */
3498 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3501 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3503 mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3506 for (f=null_font+1;f<=mp->last_fnum;f++) {
3507 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3508 for (ff=ldf;ff>= null_font;ff++) {
3509 if ( mp_has_font_size(mp,(font_number)ff) )
3510 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3513 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3515 if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(size_t)mp->max_print_line )
3516 mp_ps_print_nl(mp, "%%+ encoding");
3519 mp_ps_print_nl(mp, "%%+ encoding");
3521 mp_ps_print_char(mp, ' ');
3522 mp_ps_print(mp, mp->font_enc_name[f]);
3531 for (f=null_font+1;f<=mp->last_fnum;f++) {
3532 if ( mp_has_font_size(mp,f) ) {
3533 for (ff=ldf;ff>= null_font;ff--) {
3534 if ( mp_has_font_size(mp,(font_number)ff) )
3535 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3538 if ( ! mp_font_is_included(mp,f) )
3540 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
3541 mp_ps_print_nl(mp, "%%+ font");
3544 mp_ps_print_nl(mp, "%%+ font");
3546 mp_ps_print_char(mp, ' ');
3547 if ( mp_font_is_subsetted(mp,f) )
3548 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3550 mp_ps_print(mp, mp->font_ps_name[f]);
3560 @ @<Declarations...@>=
3561 static void mp_list_needed_resources (MP mp, int prologues);
3563 @ @c static void mp_list_needed_resources (MP mp, int prologues) {
3564 font_number f; /* fonts used in a text node or as loop counters */
3565 int ff; /* a loop counter */
3566 int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3570 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3571 if ( mp_has_font_size(mp,f)) {
3572 for (ff=ldf;ff>=null_font;ff--) {
3573 if ( mp_has_font_size(mp,(font_number)ff) )
3574 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3577 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3579 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
3580 mp_ps_print_nl(mp, "%%+ font");
3583 mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
3585 mp_ps_print_char(mp, ' ');
3586 mp_ps_print(mp, mp->font_ps_name[f]);
3592 if ( ! firstitem ) {
3596 for (f=null_font+1;f<= mp->last_fnum;f++) {
3597 if ( mp_has_font_size(mp,f) ) {
3598 for (ff=ldf;ff>=null_font;ff-- ) {
3599 if ( mp_has_font_size(mp,(font_number)ff) )
3600 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3603 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3605 mp_ps_print(mp, "%%IncludeResource: font ");
3606 mp_ps_print(mp, mp->font_ps_name[f]);
3617 static void mp_write_font_definition (MP mp, font_number f, int prologues);
3621 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3622 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3624 @c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
3625 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3626 (mp_fm_font_extend(mp,f)!=0)||
3627 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3628 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3629 if ( (mp_font_is_subsetted(mp,f))&&
3630 (mp_font_is_included(mp,f))&&(prologues==3))
3631 mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3633 mp_ps_name_out(mp, mp->font_ps_name[f],true);
3634 mp_ps_print(mp, " fcp");
3636 if ( applied_reencoding(f) ) {
3637 mp_ps_print(mp, "/Encoding ");
3638 mp_ps_print(mp, mp->font_enc_name[f]);
3639 mp_ps_print(mp, " def ");
3641 if ( mp_fm_font_slant(mp,f)!=0 ) {
3642 mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
3643 mp_ps_print(mp, " SlantFont ");
3645 if ( mp_fm_font_extend(mp,f)!=0 ) {
3646 mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3647 mp_ps_print(mp, " ExtendFont ");
3649 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3650 mp_ps_print(mp, " 890 ScaleFont ");
3651 mp_ps_print(mp, " 277 SlantFont ");
3653 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3654 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3655 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3656 mp_ps_print(mp, "/space[0 -278]def ");
3657 mp_ps_print(mp, "/a12[-904 -939]def ");
3658 mp_ps_print(mp, "end def ");
3660 mp_ps_print(mp, "currentdict end");
3662 mp_ps_print_defined_name(mp,f,prologues);
3663 mp_ps_print(mp, " exch definefont pop");
3669 static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
3672 @c static void mp_ps_print_defined_name(MP mp, font_number f, int prologues) {
3673 mp_ps_print(mp, " /");
3674 if ((mp_font_is_subsetted(mp,f))&&
3675 (mp_font_is_included(mp,f))&&(prologues==3))
3676 mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
3678 mp_ps_print(mp, mp->font_ps_name[f]);
3679 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 )
3680 mp_ps_print(mp, "-Slanted");
3681 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 )
3682 mp_ps_print(mp, "-Reverse");
3683 if ( applied_reencoding(f) ) {
3684 mp_ps_print(mp, "-");
3685 mp_ps_print(mp, mp->font_enc_name[f]);
3687 if ( mp_fm_font_slant(mp,f)!=0 ) {
3688 mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,f)) ;
3690 if ( mp_fm_font_extend(mp,f)!=0 ) {
3691 mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
3695 @ @<Include encodings and fonts for edge structure~|h|@>=
3696 mp_font_encodings(mp,mp->last_fnum,(prologues==2));
3697 @<Embed fonts that are available@>
3699 @ @<Embed fonts that are available@>=
3702 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3705 for (f=null_font+1;f<=mp->last_fnum;f++) {
3706 if ( cur_fsize[f]!=null ) {
3707 if (prologues==3 ) {
3708 if ( ! mp_do_ps_font(mp,f) ) {
3709 if ( mp_has_fm_entry(mp,f, NULL) ) {
3710 print_err("Font embedding failed");
3715 cur_fsize[f]=link(cur_fsize[f]);
3716 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3720 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3722 } while (! done_fonts);
3725 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3728 mp_apply_mark_string_chars(mp, h, next_size);
3731 @ We also need to keep track of which characters are used in text nodes
3732 in the edge structure that is being shipped out. This is done by procedures
3733 that use the left-over |b3| field in the |char_info| words; i.e.,
3734 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3737 enum mp_char_mark_state {mp_unused=0, mp_used};
3740 static void mp_mark_string_chars (MP mp,font_number f, char *s) ;
3743 void mp_mark_string_chars (MP mp,font_number f, char *s) {
3744 integer b; /* |char_base[f]| */
3745 ASCII_code bc,ec; /* only characters between these bounds are marked */
3746 char *k; /* an index into string |s| */
3752 if ( (*k>=(char)bc)&&(*k<=(char)ec) )
3753 mp->font_info[b+*k].qqqq.b3=mp_used;
3760 static void mp_unmark_font (MP mp,font_number f) ;
3763 void mp_unmark_font (MP mp,font_number f) {
3764 int k; /* an index into |font_info| */
3765 for (k= mp->char_base[f]+mp->font_bc[f];
3766 k<=mp->char_base[f]+mp->font_ec[f];
3768 mp->font_info[k].qqqq.b3=mp_unused;
3773 static void mp_print_improved_prologue (MP mp, mp_edge_object *h, int p1, int procset) ;
3776 void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
3777 quarterword next_size; /* the size index for fonts being listed */
3778 pointer *cur_fsize; /* current positions in |font_sizes| */
3779 boolean done_fonts; /* have we finished listing the fonts in the header? */
3780 font_number f; /* a font number for loops */
3781 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer));
3782 mp_list_used_resources(mp, prologues, procset);
3783 mp_list_supplied_resources(mp, prologues, procset);
3784 mp_list_needed_resources(mp, prologues);
3785 mp_ps_print_nl(mp, "%%EndComments");
3786 mp_ps_print_nl(mp, "%%BeginProlog");
3788 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
3790 mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3791 mp_ps_print_nl(mp, "/bd{bind def}bind def"
3792 "/fshow {exch findfont exch scalefont setfont show}bd");
3793 if ( procset>0 ) @<Print the procset@>;
3794 mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
3795 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3796 mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3797 "/fmd{/FontMatrix exch def}bd");
3798 mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3799 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3800 mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3801 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3802 mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3803 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3804 mp_ps_print_nl(mp, "%%EndResource");
3805 @<Include encodings and fonts for edge structure~|h|@>;
3806 mp_ps_print_nl(mp, "%%EndProlog");
3807 mp_ps_print_nl(mp, "%%BeginSetup");
3809 for (f=null_font+1;f<=mp->last_fnum;f++) {
3810 if ( mp_has_font_size(mp,f) ) {
3811 if ( mp_has_fm_entry(mp,f,NULL) ) {
3812 mp_write_font_definition(mp,f, prologues);
3813 mp_ps_name_out(mp, mp->font_name[f],true);
3814 mp_ps_print_defined_name(mp,f, prologues);
3815 mp_ps_print(mp, " def");
3818 mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3820 mp_ps_name_out(mp, mp->font_name[f],true);
3821 mp_ps_name_out(mp, mp->font_name[f],true);
3822 mp_ps_print(mp, " def");
3827 mp_ps_print_nl(mp, "%%EndSetup");
3828 mp_ps_print_nl(mp, "%%Page: 1 1");
3830 mp_xfree(cur_fsize);
3834 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues);
3839 static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) {
3840 quarterword next_size; /* the size index for fonts being listed */
3841 pointer *cur_fsize; /* current positions in |font_sizes| */
3842 int ff; /* a loop counter */
3843 boolean done_fonts; /* have we finished listing the fonts in the header? */
3844 font_number f; /* a font number for loops */
3845 scaled ds; /* design size and scale factor for a text node */
3846 int ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3847 cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer));
3848 if ( prologues>0 ) {
3849 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3850 |font_sizes| and eliminate duplicates@>;
3853 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3854 do { done_fonts=true;
3855 for (f=null_font+1;f<=mp->last_fnum;f++) {
3856 if ( cur_fsize[f]!=null ) {
3857 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3859 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
3861 if ( ! done_fonts ) {
3862 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3865 } while (! done_fonts);
3867 mp_xfree(cur_fsize);
3868 return (font_number)ldf;
3871 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3872 for (f=null_font+1;f<= mp->last_fnum;f++)
3873 cur_fsize[f]=mp->font_sizes[f]
3875 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3876 so we carefully remove duplicates. There is no harm in using a slow, brute-force
3879 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3882 for (f=null_font+1;f<= mp->last_fnum;f++) {
3883 if ( mp->font_sizes[f]!=null ) {
3884 if ( ldf==null_font )
3885 mp_ps_print_nl(mp, "%%DocumentFonts:");
3886 for (ff=ldf;ff>=null_font;ff--) {
3887 if ( mp->font_sizes[ff]!=null )
3888 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3891 if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
3892 mp_ps_print_nl(mp, "%%+");
3893 mp_ps_print_char(mp, ' ');
3894 mp_ps_print(mp, mp->font_ps_name[f]);
3903 static void mp_hex_digit_out (MP mp,quarterword d) {
3904 if ( d<10 ) mp_ps_print_char(mp, d+'0');
3905 else mp_ps_print_char(mp, d+'a'-10);
3908 @ We output the marks as a hexadecimal bit string starting at |c| or
3909 |font_bc[f]|, whichever is greater. If the output has to be truncated
3910 to avoid exceeding |emergency_line_length| the return value says where to
3911 start scanning next time.
3914 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3917 @d emergency_line_length 255
3918 /* \ps\ output lines can be this long in unusual circumstances */
3921 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3922 eight_bits bc,ec; /* only encode characters between these bounds */
3923 integer lim; /* the maximum number of marks to encode before truncating */
3924 int p; /* |font_info| index for the current character */
3925 int d; /* used to construct a hexadecimal digit */
3926 unsigned b; /* used to construct a hexadecimal digit */
3927 lim=4*(emergency_line_length-mp->ps->ps_offset-4);
3931 @<Restrict the range |bc..ec| so that it contains no unused characters
3932 at either end and has length at most |lim|@>;
3933 @<Print the initial label indicating that the bitmap starts at |bc|@>;
3934 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3935 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3941 @ We could save time by setting the return value before the loop that
3942 decrements |ec|, but there is no point in being so tricky.
3944 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3945 p=mp->char_base[f]+bc;
3946 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3949 if ( ec>=(eight_bits)(bc+lim)) ec=(eight_bits)(bc+lim-1);
3950 p=mp->char_base[f]+ec;
3951 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3955 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3956 mp_ps_print_char(mp, ' ');
3957 mp_hex_digit_out(mp, (quarterword)bc / 16);
3958 mp_hex_digit_out(mp, (quarterword)bc % 16);
3959 mp_ps_print_char(mp, ':')
3963 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3965 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3967 mp_hex_digit_out(mp, (quarterword)d);
3970 if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
3973 mp_hex_digit_out(mp, (quarterword)d)
3976 @ Here is a simple function that determines whether there are any marked
3977 characters in font~|f| with character code at least~|c|.
3980 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
3983 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
3984 int p; /* |font_info| index for the current character */
3985 for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
3986 if ( mp->font_info[p].qqqq.b3==mp_used )
3993 @ If the file name is so long that it can't be printed without exceeding
3994 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
3995 line. We might have to repeat line in order to get the character usage
3996 information to fit within |emergency_line_length|.
3998 TODO: these two defines are also defined in mp.w!
4000 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
4001 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
4003 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
4005 while ( mp_check_ps_marks(mp, f,t) ) {
4006 mp_ps_print_nl(mp, "%*Font: ");
4007 if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
4009 mp_ps_print(mp, mp->font_name[f]);
4010 mp_ps_print_char(mp, ' ');
4011 ds=(mp->font_dsize[f] + 8) / 16;
4012 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
4013 if ( mp->ps->ps_offset+12>emergency_line_length ) break;
4014 mp_ps_print_char(mp, ' ');
4015 mp_ps_print_scaled(mp, ds);
4016 if ( mp->ps->ps_offset+5>emergency_line_length ) break;
4017 t=mp_ps_marks_out(mp, f, (eight_bits)t);
4019 cur_fsize[f]=link(cur_fsize[f]);
4022 @ @<Print the procset@>=
4024 mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
4025 mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
4026 mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
4027 "/p{closepath}bd/n{newpath}bd");
4028 mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
4029 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
4030 mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
4031 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
4032 mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
4036 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
4037 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
4038 might not have reasonable |font_ps_name| entries, but we just charge ahead
4039 anyway. The user should not make \&{prologues} positive if this will cause
4041 @:prologues_}{\&{prologues} primitive@>
4044 static void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset);
4047 void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
4050 ldf = mp_print_font_comments (mp, h, prologues);
4052 if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
4053 mp_read_psname_table(mp);
4054 mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
4055 if ( (prologues>0)||(procset>0) ) {
4056 if ( ldf!=null_font ) {
4057 if ( prologues>0 ) {
4058 for (f=null_font+1;f<=mp->last_fnum;f++) {
4059 if ( mp_has_font_size(mp,f) ) {
4060 mp_ps_name_out(mp, mp->font_name[f],true);
4061 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4062 mp_ps_print(mp, " def");
4067 mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4073 mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
4074 if ( (prologues>0)&&(ldf!=null_font) )
4076 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4078 mp_ps_print_nl(mp, "/bd{bind def}bind def");
4079 @<Print the procset@>;
4080 mp_ps_print_nl(mp, "%%EndResource");
4084 mp_ps_print(mp, "%%EndProlog");
4085 mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
4088 @ \MP\ used to have one single routine to print to both `write' files
4089 and the PostScript output. Web2c redefines ``Character |k| cannot be
4090 printed'', and that resulted in some bugs where 8-bit characters were
4091 written to the PostScript file (reported by Wlodek Bzyl).
4093 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4094 a plain space, since that makes it easier to parse the result file
4097 @<Character |k| is not allowed in PostScript output@>=
4100 @ We often need to print a pair of coordinates.
4103 void mp_ps_pair_out (MP mp,scaled x, scaled y) {
4105 mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
4106 mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
4110 static void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4113 void mp_ps_print_cmd (MP mp, const char *l, const char *s) {
4114 if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
4115 else { ps_room(strlen(l)); mp_ps_print(mp, l); };
4119 static void mp_ps_print_cmd (MP mp, const char *l, const char *s) ;
4122 void mp_ps_string_out (MP mp, const char *s) {
4123 ASCII_code k; /* bits to be converted to octal */
4124 mp_ps_print(mp, "(");
4125 while ((k=(ASCII_code)*s++)) {
4126 if ( mp->ps->ps_offset+5>mp->max_print_line ) {
4127 mp_ps_print_char(mp, '\\');
4130 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4131 mp_ps_print_char(mp, '\\');
4132 mp_ps_print_char(mp, '0'+(k / 64));
4133 mp_ps_print_char(mp, '0'+((k / 8) % 8));
4134 mp_ps_print_char(mp, '0'+(k % 8));
4136 if ( (k=='(')||(k==')')||(k=='\\') )
4137 mp_ps_print_char(mp, '\\');
4138 mp_ps_print_char(mp, k);
4141 mp_ps_print_char(mp, ')');
4145 static void mp_ps_string_out (MP mp, const char *s) ;
4147 @ This is a define because the function does not use its |mp| argument.
4149 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4152 static boolean mp_do_is_ps_name (char *s) {
4153 ASCII_code k; /* the character being checked */
4154 while ((k=(ASCII_code)*s++)) {
4155 if ( (k<=' ')||(k>'~') ) return false;
4156 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4157 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4163 static void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4166 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4167 ps_room(strlen(s)+2);
4168 mp_ps_print_char(mp, ' ');
4169 if ( mp_is_ps_name(mp, s) ) {
4170 if ( lit ) mp_ps_print_char(mp, '/');
4173 mp_ps_string_out(mp, s);
4174 if ( ! lit ) mp_ps_print(mp, "cvx ");
4175 mp_ps_print(mp, "cvn");
4180 @ These special comments described in the {\sl PostScript Language Reference
4181 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4182 We can't normally output ``conforming'' \ps\ because
4183 the structuring conventions don't allow us to say ``Please make sure the
4184 following characters are downloaded and define the \.{fshow} macro to access
4187 The exact bounding box is written out if |mp_prologues<0|, although this
4188 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4189 accurately. (Overfull boxes are avoided if an illustration is made to
4190 match a given \.{\char`\\hsize}.)
4193 static void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues);
4196 void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) {
4198 mp_ps_print(mp, "%!PS");
4200 mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
4201 mp_ps_print_nl(mp, "%%BoundingBox: ");
4202 if ( hh->minx>hh->maxx) {
4203 mp_ps_print(mp, "0 0 0 0");
4204 } else if ( prologues<0 ) {
4205 mp_ps_pair_out(mp, hh->minx,hh->miny);
4206 mp_ps_pair_out(mp, hh->maxx,hh->maxy);
4208 mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->minx),mp_floor_scaled(mp, hh->miny));
4209 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->maxx),-mp_floor_scaled(mp, -hh->maxy));
4211 mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
4212 if ( hh->minx>hh->maxx ) {
4213 mp_ps_print(mp, "0 0 0 0");
4215 mp_ps_pair_out(mp, hh->minx,hh->miny);
4216 mp_ps_pair_out(mp, hh->maxx,hh->maxy);
4218 mp_ps_print_nl(mp, "%%Creator: MetaPost ");
4219 mp_ps_print(mp, mp_metapost_version());
4220 mp_ps_print_nl(mp, "%%CreationDate: ");
4221 mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
4222 mp_ps_print_char(mp, '.');
4223 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month]));
4224 mp_ps_print_char(mp, '.');
4225 mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
4226 mp_ps_print_char(mp, ':');
4227 t=mp_round_unscaled(mp, mp->internal[mp_time]);
4228 mp_ps_print_dd(mp, t / 60);
4229 mp_ps_print_dd(mp, t % 60);
4230 mp_ps_print_nl(mp, "%%Pages: 1");
4233 @ The most important output procedure is the one that gives the \ps\ version of
4237 typedef struct mp_knot {
4238 unsigned short left_type;
4239 unsigned short right_type;
4246 struct mp_knot * next;
4247 unsigned char originator;
4251 #define gr_left_type(A) (A)->left_type
4252 #define gr_right_type(A) (A)->right_type
4253 #define gr_x_coord(A) (A)->x_coord
4254 #define gr_y_coord(A) (A)->y_coord
4255 #define gr_left_x(A) (A)->left_x
4256 #define gr_left_y(A) (A)->left_y
4257 #define gr_right_x(A) (A)->right_x
4258 #define gr_right_y(A) (A)->right_y
4259 #define gr_next_knot(A) (A)->next
4260 #define gr_originator(A) (A)->originator
4262 @ If we want to duplicate a knot node, we can say |copy_knot|:
4265 static mp_knot *mp_gr_copy_knot (MP mp, mp_knot *p) {
4266 mp_knot *q; /* the copy */
4267 q = mp_xmalloc(mp, 1, sizeof (mp_knot));
4268 memcpy(q,p,sizeof (mp_knot));
4269 gr_next_knot(q)=NULL;
4273 @ The |copy_path| routine makes a clone of a given path.
4276 static mp_knot *mp_gr_copy_path (MP mp, mp_knot *p) {
4277 mp_knot *q, *pp, *qq; /* for list manipulation */
4280 q=mp_gr_copy_knot(mp, p);
4284 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4285 qq=gr_next_knot(qq);
4286 pp=gr_next_knot(pp);
4292 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4293 returns a pointer to the first node of the copy, if the path is a cycle,
4294 but to the final node of a non-cyclic copy. The global
4295 variable |path_tail| will point to the final node of the original path;
4296 this trick makes it easier to implement `\&{doublepath}'.
4298 All node types are assumed to be |endpoint| or |explicit| only.
4300 This function is currenly unused.
4303 mp_knot * mp_gr_htap_ypoc (MP mp, mp_knot *p) {
4304 mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4305 q=mp_xmalloc(mp, 1, sizeof (mp_knot)); /* this will correspond to |p| */
4308 gr_right_type(qq)=gr_left_type(pp);
4309 gr_left_type(qq)=gr_right_type(pp);
4310 gr_x_coord(qq)=gr_x_coord(pp);
4311 gr_y_coord(qq)=gr_y_coord(pp);
4312 gr_right_x(qq)=gr_left_x(pp);
4313 gr_right_y(qq)=gr_left_y(pp);
4314 gr_left_x(qq)=gr_right_x(pp);
4315 gr_left_y(qq)=gr_right_y(pp);
4316 gr_originator(qq)=gr_originator(pp);
4317 if ( gr_next_knot(pp)==p ) {
4319 /* |mp->path_tail=pp;| */ /* ? */
4322 rr=mp_xmalloc(mp, 1, sizeof (mp_knot));
4323 gr_next_knot(rr)=qq;
4325 pp=gr_next_knot(pp);
4329 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4330 calling the following subroutine.
4333 static void mp_do_gr_toss_knot_list (mp_knot *p) ;
4336 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4339 void mp_do_gr_toss_knot_list (mp_knot * p) {
4340 mp_knot *q; /* the node being freed */
4341 mp_knot *r; /* the next node */
4354 static void mp_gr_ps_path_out (MP mp, mp_knot *h) {
4355 mp_knot *p, *q; /* for scanning the path */
4356 scaled d; /* a temporary value */
4357 boolean curved; /* |true| unless the cubic is almost straight */
4359 mp_ps_print_cmd(mp, "newpath ","n ");
4360 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4361 mp_ps_print_cmd(mp, "moveto","m");
4364 if ( gr_right_type(p)==mp_endpoint ) {
4365 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4369 @<Start a new line and print the \ps\ commands for the curve from
4373 mp_ps_print_cmd(mp, " closepath"," p");
4376 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4378 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4381 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4382 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4383 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4384 mp_ps_print_cmd(mp, "curveto","c");
4386 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4387 mp_ps_print_cmd(mp, "lineto","l");
4390 @ Two types of straight lines come up often in \MP\ paths:
4391 cubics with zero initial and final velocity as created by |make_path| or
4392 |make_envelope|, and cubics with control points uniformly spaced on a line
4393 as created by |make_choices|.
4395 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4397 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4398 if ( gr_right_x(p)==gr_x_coord(p) )
4399 if ( gr_right_y(p)==gr_y_coord(p) )
4400 if ( gr_left_x(q)==gr_x_coord(q) )
4401 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4402 d=gr_left_x(q)-gr_right_x(p);
4403 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4404 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4405 { d=gr_left_y(q)-gr_right_y(p);
4406 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4407 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4410 @ The colored objects use a struct with anonymous fields to express the color parts:
4414 int a_val, b_val, c_val, d_val;
4417 @ The exported form of a dash pattern is simpler than the internal
4418 format, it is closely modelled to the PostScript model. The array of
4419 dashes is ended by a single negative value, because this is not
4420 allowed in PostScript.
4430 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
4433 static void mp_do_gr_toss_dashes(mp_dash_object *dl);
4436 void mp_do_gr_toss_dashes(mp_dash_object *dl) {
4439 mp_xfree(dl->array);
4445 static mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) {
4446 mp_dash_object *q = NULL;
4450 q = mp_xmalloc(mp, 1, sizeof (mp_dash_object));
4451 memcpy (q,dl,sizeof(mp_dash_object));
4452 if (dl->array != NULL) {
4454 while (*(dl->array+i) != -1) i++;
4455 q->array = mp_xmalloc(mp, i, sizeof (scaled));
4456 memcpy(q->array,dl->array, (i*sizeof(scaled)));
4462 @ Now for outputting the actual graphic objects. First, set up some
4463 structures and access macros.
4465 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4468 #define gr_type(A) (A)->type
4469 #define gr_link(A) (A)->next
4470 #define gr_color_model(A) (A)->color_model
4471 #define gr_red_val(A) (A)->color.a_val
4472 #define gr_green_val(A) (A)->color.b_val
4473 #define gr_blue_val(A) (A)->color.c_val
4474 #define gr_cyan_val(A) (A)->color.a_val
4475 #define gr_magenta_val(A) (A)->color.b_val
4476 #define gr_yellow_val(A) (A)->color.c_val
4477 #define gr_black_val(A) (A)->color.d_val
4478 #define gr_grey_val(A) (A)->color.a_val
4479 #define gr_path_p(A) (A)->path_p
4480 #define gr_htap_p(A) ((mp_fill_object *)A)->htap_p
4481 #define gr_pen_p(A) (A)->pen_p
4482 #define gr_ljoin_val(A) (A)->ljoin
4483 #define gr_lcap_val(A) ((mp_stroked_object *)A)->lcap
4484 #define gr_miterlim_val(A) (A)->miterlim
4485 #define gr_pre_script(A) (A)->pre_script
4486 #define gr_post_script(A) (A)->post_script
4487 #define gr_dash_p(A) ((mp_stroked_object *)A)->dash_p
4488 #define gr_size_index(A) ((mp_text_object *)A)->size_index
4489 #define gr_text_p(A) ((mp_text_object *)A)->text_p
4490 #define gr_font_n(A) ((mp_text_object *)A)->font_n
4491 #define gr_font_name(A) ((mp_text_object *)A)->font_name
4492 #define gr_font_dsize(A) ((mp_text_object *)A)->font_dsize
4493 #define gr_width_val(A) ((mp_text_object *)A)->width
4494 #define gr_height_val(A) ((mp_text_object *)A)->height
4495 #define gr_depth_val(A) ((mp_text_object *)A)->depth
4496 #define gr_tx_val(A) ((mp_text_object *)A)->tx
4497 #define gr_ty_val(A) ((mp_text_object *)A)->ty
4498 #define gr_txx_val(A) ((mp_text_object *)A)->txx
4499 #define gr_txy_val(A) ((mp_text_object *)A)->txy
4500 #define gr_tyx_val(A) ((mp_text_object *)A)->tyx
4501 #define gr_tyy_val(A) ((mp_text_object *)A)->tyy
4504 #define GRAPHIC_BODY \
4506 struct mp_graphic_object * next
4508 typedef struct mp_graphic_object {
4510 } mp_graphic_object;
4512 typedef struct mp_text_object {
4517 unsigned char color_model;
4518 unsigned char size_index;
4521 unsigned int font_dsize ;
4522 unsigned int font_n ;
4534 typedef struct mp_fill_object {
4539 unsigned char color_model;
4540 unsigned char ljoin ;
4547 typedef struct mp_stroked_object {
4552 unsigned char color_model;
4553 unsigned char ljoin ;
4554 unsigned char lcap ;
4558 mp_dash_object *dash_p;
4559 } mp_stroked_object;
4561 typedef struct mp_clip_object {
4566 typedef struct mp_bounds_object {
4571 typedef struct mp_special_object {
4574 } mp_special_object ;
4576 typedef struct mp_edge_object {
4577 struct mp_graphic_object * body;
4578 struct mp_edge_object * next;
4581 int minx, miny, maxx, maxy;
4582 int width, height, depth, ital_corr;
4586 @ @<Exported function headers@>=
4587 mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4590 mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4591 mp_graphic_object *p;
4594 case mp_fill_code: size = sizeof(mp_fill_object); break;
4595 case mp_stroked_code: size = sizeof(mp_stroked_object); break;
4596 case mp_text_code: size = sizeof(mp_text_object); break;
4597 case mp_start_clip_code: size = sizeof(mp_clip_object); break;
4598 case mp_start_bounds_code: size = sizeof(mp_bounds_object); break;
4599 case mp_special_code: size = sizeof(mp_special_object); break;
4600 default: size = sizeof(mp_graphic_object); break;
4602 p = (mp_graphic_object *)mp_xmalloc(mp,1,size);
4608 @ We need to keep track of several parameters from the \ps\ graphics state.
4610 This allows us to be sure that \ps\ has the correct values when they are
4611 needed without wasting time and space setting them unnecessarily.
4613 @d gs_red mp->ps->gs_state->red_field
4614 @d gs_green mp->ps->gs_state->green_field
4615 @d gs_blue mp->ps->gs_state->blue_field
4616 @d gs_black mp->ps->gs_state->black_field
4617 @d gs_colormodel mp->ps->gs_state->colormodel_field
4618 @d gs_ljoin mp->ps->gs_state->ljoin_field
4619 @d gs_lcap mp->ps->gs_state->lcap_field
4620 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4621 @d gs_miterlim mp->ps->gs_state->miterlim_field
4622 @d gs_dash_p mp->ps->gs_state->dash_p_field
4623 @d gs_dash_init_done mp->ps->gs_state->dash_done_field
4624 @d gs_previous mp->ps->gs_state->previous_field
4625 @d gs_width mp->ps->gs_state->width_field
4628 typedef struct _gs_state {
4630 scaled green_field ;
4632 scaled black_field ;
4633 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4634 quarterword colormodel_field ;
4635 /* the current colormodel */
4636 quarterword ljoin_field ;
4637 quarterword lcap_field ;
4638 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4639 quarterword adj_wx_field ;
4640 /* what resolution-dependent adjustment applies to the width */
4641 scaled miterlim_field ;
4642 /* the value from the last \&{setmiterlimit} command */
4643 mp_dash_object * dash_p_field ;
4644 /* edge structure for last \&{setdash} command */
4645 boolean dash_done_field ; /* to test for initial \&{setdash} */
4646 struct _gs_state * previous_field ;
4647 /* backlink to the previous |_gs_state| structure */
4648 scaled width_field ;
4649 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4654 struct _gs_state * gs_state;
4657 mp->ps->gs_state=NULL;
4659 @ @<Dealloc variables@>=
4660 mp_xfree(mp->ps->gs_state);
4662 @ To avoid making undue assumptions about the initial graphics state, these
4663 parameters are given special values that are guaranteed not to match anything
4664 in the edge structure being shipped out. On the other hand, the initial color
4665 should be black so that the translation of an all-black picture will have no
4666 \&{setcolor} commands. (These would be undesirable in a font application.)
4667 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4668 to recover from a situation where we have lost track of the graphics state.
4670 @d mp_void (null+1) /* a null pointer different from |null| */
4672 @c static void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4673 struct _gs_state *p; /* to shift graphic states around */
4674 if ( (c==0)||(c==-1) ) {
4675 if ( mp->ps->gs_state==NULL ) {
4676 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4679 while ( gs_previous!=NULL ) {
4681 mp_xfree(mp->ps->gs_state);
4685 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4686 gs_colormodel=mp_uninitialized_model;
4691 gs_dash_init_done=false;
4693 } else if ( c==1 ) {
4694 p= mp->ps->gs_state;
4695 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4696 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4698 } else if ( c==2 ) {
4700 mp_xfree(mp->ps->gs_state);
4706 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4707 that \ps's idea of the graphics state agrees with what is stored in the object.
4710 static void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ;
4713 void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) {
4714 /* get ready to output graphical object |p| */
4715 mp_knot *pp, *path_p; /* for list manipulation */
4717 scaled wx,wy,ww; /* dimensions of pen bounding box */
4718 quarterword adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4719 integer tx,ty; /* temporaries for computing |adj_wx| */
4720 if ( gr_has_color(p) )
4721 @<Make sure \ps\ will use the right color for object~|p|@>;
4722 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) {
4723 if (gr_type(p)==mp_fill_code) {
4724 pp = gr_pen_p((mp_fill_object *)p);
4725 path_p = gr_path_p((mp_fill_object *)p);
4727 pp = gr_pen_p((mp_stroked_object *)p);
4728 path_p = gr_path_p((mp_stroked_object *)p);
4731 if ( pen_is_elliptical(pp) ) {
4732 @<Generate \ps\ code that sets the stroke width to the
4733 appropriate rounded value@>;
4734 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4735 @<Decide whether the line cap parameter matters and set it if necessary@>;
4736 @<Set the other numeric parameters as needed for object~|p|@>;
4739 if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
4742 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4743 if ( gr_type(p)==mp_stroked_code ) {
4744 mp_stroked_object *ts = (mp_stroked_object *)p;
4745 if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) )
4746 if ( gs_lcap!=(quarterword)gr_lcap_val(ts) ) {
4748 mp_ps_print_char(mp, ' ');
4749 mp_ps_print_char(mp, '0'+gr_lcap_val(ts));
4750 mp_ps_print_cmd(mp, " setlinecap"," lc");
4751 gs_lcap=(quarterword)gr_lcap_val(ts);
4756 @d set_ljoin_miterlim(p)
4757 if ( gs_ljoin!=(quarterword)gr_ljoin_val(p) ) {
4759 mp_ps_print_char(mp, ' ');
4760 mp_ps_print_char(mp, '0'+gr_ljoin_val(p));
4761 mp_ps_print_cmd(mp, " setlinejoin"," lj");
4762 gs_ljoin=(quarterword)gr_ljoin_val(p);
4764 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4766 mp_ps_print_char(mp, ' ');
4767 mp_ps_print_scaled(mp, gr_miterlim_val(p));
4768 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4769 gs_miterlim=gr_miterlim_val(p);
4772 @<Set the other numeric parameters as needed for object~|p|@>=
4773 if ( gr_type(p)==mp_stroked_code ) {
4774 mp_stroked_object *ts = (mp_stroked_object *)p;
4775 set_ljoin_miterlim(ts);
4777 mp_fill_object *ts = (mp_fill_object *)p;
4778 set_ljoin_miterlim(ts);
4782 @d set_color_objects(pq)
4783 object_color_model = pq->color_model;
4784 object_color_a = pq->color.a_val;
4785 object_color_b = pq->color.b_val;
4786 object_color_c = pq->color.c_val;
4787 object_color_d = pq->color.d_val;
4789 @<Make sure \ps\ will use the right color for object~|p|@>=
4791 int object_color_model;
4792 int object_color_a, object_color_b, object_color_c, object_color_d ;
4793 if (gr_type(p) == mp_fill_code) {
4794 mp_fill_object *pq = (mp_fill_object *)p;
4795 set_color_objects(pq);
4796 } else if (gr_type(p) == mp_stroked_code) {
4797 mp_stroked_object *pq = (mp_stroked_object *)p;
4798 set_color_objects(pq);
4800 mp_text_object *pq = (mp_text_object *)p;
4801 set_color_objects(pq);
4804 if ( object_color_model==mp_rgb_model) {
4805 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)||
4806 (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) {
4807 gs_red = object_color_a;
4808 gs_green = object_color_b;
4809 gs_blue = object_color_c;
4811 gs_colormodel=mp_rgb_model;
4813 mp_ps_print_char(mp, ' ');
4814 mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
4815 mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
4816 mp_ps_print_scaled(mp, gs_blue);
4817 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4820 } else if ( object_color_model==mp_cmyk_model) {
4821 if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)||
4822 (gs_blue!=object_color_c)||(gs_black!=object_color_d)||
4823 (gs_colormodel!=mp_cmyk_model) ) {
4824 gs_red = object_color_a;
4825 gs_green = object_color_b;
4826 gs_blue = object_color_c;
4827 gs_black = object_color_d;
4828 gs_colormodel=mp_cmyk_model;
4830 mp_ps_print_char(mp, ' ');
4831 mp_ps_print_scaled(mp, gs_red);
4832 mp_ps_print_char(mp, ' ');
4833 mp_ps_print_scaled(mp, gs_green);
4834 mp_ps_print_char(mp, ' ');
4835 mp_ps_print_scaled(mp, gs_blue);
4836 mp_ps_print_char(mp, ' ');
4837 mp_ps_print_scaled(mp, gs_black);
4838 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4841 } else if ( object_color_model==mp_grey_model ) {
4842 if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) {
4843 gs_red = object_color_a;
4847 gs_colormodel=mp_grey_model;
4849 mp_ps_print_char(mp, ' ');
4850 mp_ps_print_scaled(mp, gs_red);
4851 mp_ps_print_cmd(mp, " setgray"," G");
4854 } else if ( object_color_model==mp_no_model ) {
4855 gs_colormodel=mp_no_model;
4859 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4860 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4861 @:setwidth}{\&{setwidth}command@>
4862 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4863 generate \ps\ code that computes the rounded value. For non-circular pens, the
4864 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4865 have the computed width.
4867 Rounding the width to whole pixels is not likely to improve the appearance of
4868 diagonal or curved strokes, but we do it anyway for consistency. The
4869 \&{truncate} command generated here tends to make all the strokes a little
4870 @:truncate}{\&{truncate} command@>
4871 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4872 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4873 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4876 @<Generate \ps\ code that sets the stroke width...@>=
4877 @<Set |wx| and |wy| to the width and height of the bounding box for
4879 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4880 important and set |adj_wx| and |ww| accordingly@>;
4881 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4882 if ( adj_wx != 0 ) {
4884 mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
4886 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4888 if ( mp->internal[mp_procset]>0 ) {
4890 mp_ps_print_char(mp, ' ');
4891 mp_ps_print_scaled(mp, ww);
4892 mp_ps_print(mp, " vlw");
4895 mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
4896 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4903 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4904 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4905 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4906 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4908 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4909 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4912 @ The path is considered ``essentially horizontal'' if its range of
4913 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4914 vertical'' paths are detected similarly. This code ensures that no component
4915 of the pen transformation is more that |aspect_bound*(ww+1)|.
4917 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4918 more than this factor */
4923 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4925 if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound;
4926 else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound;
4927 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=0; }
4928 else { ww=wx; adj_wx=1; }
4930 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4931 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4932 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4933 bounding-box computation.
4936 static boolean mp_gr_coord_rangeOK (mp_knot *h,
4937 quarterword zoff, scaled dz);
4940 boolean mp_gr_coord_rangeOK (mp_knot *h,
4941 quarterword zoff, scaled dz) {
4942 mp_knot *p; /* for scanning the path form |h| */
4943 scaled zlo,zhi; /* coordinate range so far */
4944 scaled z; /* coordinate currently being tested */
4945 if (zoff==do_x_loc) {
4949 while ( gr_right_type(p)!=mp_endpoint ) {
4951 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4952 p=gr_next_knot(p); z=gr_left_x(p);
4953 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4955 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4962 while ( gr_right_type(p)!=mp_endpoint ) {
4964 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4965 p=gr_next_knot(p); z=gr_left_y(p);
4966 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4968 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4975 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4977 else if ( z>zhi ) zhi=z;
4978 if ( zhi-zlo>dz ) return false
4980 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4981 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4982 @:stroke}{\&{stroke} command@>
4983 @:fill}{\&{fill} command@>
4984 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4987 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4988 if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) {
4994 if ( gs_dash_p!=NULL || gs_dash_init_done == false) {
4995 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4997 gs_dash_init_done=true;
4999 } else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) {
5000 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
5003 @ The original code had a check here to ensure that the result from
5004 |mp_take_scaled| did not go out of bounds.
5006 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
5008 if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array==NULL)) {
5009 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
5013 mp_ps_print(mp, " [");
5014 for (i=0; *(hh->array+i) != -1;i++) {
5016 mp_ps_print_scaled(mp, *(hh->array+i));
5017 mp_ps_print_char(mp, ' ') ;
5020 mp_ps_print(mp, "] ");
5021 mp_ps_print_scaled(mp, hh->offset);
5022 mp_ps_print_cmd(mp, " setdash"," sd");
5027 static boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ;
5029 @ This function test if |h| and |hh| represent the same dash pattern.
5032 boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) {
5035 if ( h==hh ) ret=true;
5036 else if ( (h==NULL)||(hh==NULL) ) ret=false;
5037 else if ( h->offset!=hh->offset ) ret=false;
5038 else if ( h->array == hh->array ) ret=true;
5039 else if ( h->array == NULL || hh->array == NULL) ret=false;
5040 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
5044 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
5046 while (*(h->array+i)!=-1 &&
5047 *(hh->array+i)!=-1 &&
5048 *(h->array+i) == *(hh->array+i)) i++;
5050 if (*(h->array+i)==-1 && *(hh->array+i) == -1)
5055 @ When stroking a path with an elliptical pen, it is necessary to transform
5056 the coordinate system so that a unit circular pen will have the desired shape.
5057 To keep this transformation local, we enclose it in a
5058 $$\&{gsave}\ldots\&{grestore}$$
5059 block. Any translation component must be applied to the path being stroked
5060 while the rest of the transformation must apply only to the pen.
5061 If |fill_also=true|, the path is to be filled as well as stroked so we must
5062 insert commands to do this after giving the path.
5065 static void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) ;
5068 @c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) {
5069 /* generate an elliptical pen stroke from object |h| */
5070 scaled txx,txy,tyx,tyy; /* transformation parameters */
5071 mp_knot *p; /* the pen to stroke with */
5072 scaled d1,det; /* for tweaking transformation parameters */
5073 integer s; /* also for tweaking transformation paramters */
5074 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5076 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5078 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5079 if (gr_type(h)==mp_fill_code) {
5080 mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h));
5082 mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h));
5084 if ( mp->internal[mp_procset]==0 ) {
5085 if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
5086 @<Issue \ps\ commands to transform the coordinate system@>;
5087 mp_ps_print(mp, " stroke");
5088 if ( transformed ) mp_ps_print(mp, " grestore");
5090 if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
5091 if ( (txy!=0)||(tyx!=0) ) {
5092 mp_ps_print(mp, " [");
5093 mp_ps_pair_out(mp, txx,tyx);
5094 mp_ps_pair_out(mp, txy,tyy);
5095 mp_ps_print(mp, "0 0] t");
5096 } else if ((txx!=unity)||(tyy!=unity) ) {
5097 mp_ps_pair_out(mp,txx,tyy);
5098 mp_ps_print(mp, " s");
5100 mp_ps_print(mp, " S");
5101 if ( transformed ) mp_ps_print(mp, " Q");
5106 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5107 if (gr_type(h)==mp_fill_code) {
5108 p=gr_pen_p((mp_fill_object *)h);
5110 p=gr_pen_p((mp_stroked_object *)h);
5116 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5117 mp_ps_print_nl(mp, "");
5118 mp_ps_print_cmd(mp, "gsave ","q ");
5119 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5120 mp_ps_print(mp, "translate ");
5127 mp_ps_print_nl(mp, "");
5129 @<Adjust the transformation to account for |gs_width| and output the
5130 initial \&{gsave} if |transformed| should be |true|@>
5132 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5133 if ( gs_width!=unity ) {
5134 if ( gs_width==0 ) {
5135 txx=unity; tyy=unity;
5137 txx=mp_make_scaled(mp, txx,gs_width);
5138 txy=mp_make_scaled(mp, txy,gs_width);
5139 tyx=mp_make_scaled(mp, tyx,gs_width);
5140 tyy=mp_make_scaled(mp, tyy,gs_width);
5143 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5144 if ( (! transformed) ){
5145 mp_ps_print_cmd(mp, "gsave ","q ");
5150 @ @<Issue \ps\ commands to transform the coordinate system@>=
5151 if ( (txy!=0)||(tyx!=0) ){
5153 mp_ps_print_char(mp, '[');
5154 mp_ps_pair_out(mp, txx,tyx);
5155 mp_ps_pair_out(mp, txy,tyy);
5156 mp_ps_print(mp, "0 0] concat");
5157 } else if ( (txx!=unity)||(tyy!=unity) ){
5159 mp_ps_pair_out(mp, txx,tyy);
5160 mp_ps_print(mp, "scale");
5163 @ The \ps\ interpreter will probably abort if it encounters a singular
5164 transformation matrix. The determinant must be large enough to ensure that
5165 the printed representation will be nonsingular. Since the printed
5166 representation is always within $2^{-17}$ of the internal |scaled| value, the
5167 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5168 the magnitudes of |txx/65536|, |txy/65536|, etc.
5170 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5171 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5173 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5174 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5175 d1=4*aspect_bound+1;
5176 if ( abs(det)<d1 ) {
5177 if ( det>=0 ) { d1=d1-det; s=1; }
5178 else { d1=-d1-det; s=-1; };
5180 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5181 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5182 else txx=txx+(d1+s*abs(tyy)) / tyy;
5184 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5185 else txy=txy+(d1+s*abs(tyx)) / tyx;
5189 @ Here is a simple routine that just fills a cycle.
5192 static void mp_gr_ps_fill_out (MP mp, mp_knot *p);
5195 void mp_gr_ps_fill_out (MP mp, mp_knot *p) { /* fill cyclic path~|p| */
5196 mp_gr_ps_path_out(mp, p);
5197 mp_ps_print_cmd(mp, " fill"," F");
5201 @ A text node may specify an arbitrary transformation but the usual case
5202 involves only shifting, scaling, and occasionally rotation. The purpose
5203 of |choose_scale| is to select a scale factor so that the remaining
5204 transformation is as ``nice'' as possible. The definition of ``nice''
5205 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5206 nice because they work out well for bitmap fonts. The code here selects
5207 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5208 non-shifting part of the transformation matrix. It is careful to avoid
5209 additions that might cause undetected overflow.
5212 static scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) ;
5214 @ @c scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) {
5215 /* |p| should point to a text node */
5216 scaled a,b,c,d,ad,bc; /* temporary values */
5221 if ( a<0 ) negate(a);
5222 if ( b<0 ) negate(b);
5223 if ( c<0 ) negate(c);
5224 if ( d<0 ) negate(d);
5227 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5230 @ The potential overflow here is caused by the fact the returned value
5231 has to fit in a |name_type|, which is a quarterword.
5233 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
5236 static quarterword mp_size_index (MP mp, font_number f, scaled s) ;
5239 quarterword mp_size_index (MP mp, font_number f, scaled s) {
5240 pointer p,q; /* the previous and current font size nodes */
5241 quarterword i; /* the size index for |q| */
5242 q=mp->font_sizes[f];
5245 if ( abs(s-sc_factor(q))<=fscale_tolerance )
5248 { p=q; q=link(q); incr(i); };
5249 if ( i==max_quarterword )
5250 mp_overflow(mp, "sizes per font",max_quarterword);
5251 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
5253 q=mp_get_node(mp, font_size_size);
5255 if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
5260 static scaled mp_indexed_size (MP mp,font_number f, quarterword j);
5263 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
5264 pointer p; /* a font size node */
5265 quarterword i; /* the size index for |p| */
5266 p=mp->font_sizes[f];
5268 if ( p==null ) mp_confusion(mp, "size");
5271 if ( p==null ) mp_confusion(mp, "size");
5273 return sc_factor(p);
5277 static void mp_clear_sizes (MP mp) ;
5279 @ @c void mp_clear_sizes (MP mp) {
5280 font_number f; /* the font whose size list is being cleared */
5281 pointer p; /* current font size nodes */
5282 for (f=null_font+1;f<=mp->last_fnum;f++) {
5283 while ( mp->font_sizes[f]!=null ) {
5284 p=mp->font_sizes[f];
5285 mp->font_sizes[f]=link(p);
5286 mp_free_node(mp, p,font_size_size);
5291 @ A text node may specify an arbitrary transformation but the usual case
5292 involves only shifting, scaling, and occasionally rotation. The purpose
5293 of |choose_scale| is to select a scale factor so that the remaining
5294 transformation is as ``nice'' as possible. The definition of ``nice''
5295 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5296 nice because they work out well for bitmap fonts. The code here selects
5297 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5298 non-shifting part of the transformation matrix. It is careful to avoid
5299 additions that might cause undetected overflow.
5302 @ @c static scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
5303 /* |p| should point to a text node */
5304 scaled a,b,c,d,ad,bc; /* temporary values */
5309 if ( (a<0) ) negate(a);
5310 if ( (b<0) ) negate(b);
5311 if ( (c<0) ) negate(c);
5312 if ( (d<0) ) negate(d);
5315 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5318 @ There may be many sizes of one font and we need to keep track of the
5319 characters used for each size. This is done by keeping a linked list of
5320 sizes for each font with a counter in each text node giving the appropriate
5321 position in the size list for its font.
5323 @d font_size_size 2 /* size of a font size node */
5327 static void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size);
5330 void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) {
5331 mp_graphic_object * p;
5333 while ( p!= NULL ) {
5334 if ( gr_type(p)==mp_text_code ) {
5335 if ( gr_font_n(p)!=null_font ) {
5336 if ( gr_size_index(p)==(unsigned char)next_size )
5337 mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p));
5344 @ @<Unmark all marked characters@>=
5345 for (f=null_font+1;f<=mp->last_fnum;f++) {
5346 if ( mp->font_sizes[f]!=null ) {
5347 mp_unmark_font(mp, f);
5348 mp->font_sizes[f]=null;
5352 @ @<Scan all the text nodes and mark the used ...@>=
5355 if ( gr_type(p)==mp_text_code ) {
5357 if (f!=null_font ) {
5358 switch (prologues) {
5361 mp->font_sizes[f] = mp_void;
5362 mp_mark_string_chars(mp, f, gr_text_p(p));
5363 if (mp_has_fm_entry(mp,f,NULL) ) {
5364 if (mp->font_enc_name[f]==NULL )
5365 mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
5366 mp->font_ps_name[f] = mp_fm_font_name(mp,f);
5370 mp->font_sizes[f]=mp_void;
5373 gr_size_index(p)=(unsigned char)mp_size_index(mp, f,mp_choose_scale(mp, p));
5374 if ( gr_size_index(p)==0 )
5375 mp_mark_string_chars(mp, f, gr_text_p(p));
5384 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5386 @<Exported function ...@>=
5387 int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) ;
5390 int mp_gr_ship_out (mp_edge_object *hh, int qprologues, int qprocset,int standalone) {
5391 mp_graphic_object *p;
5392 scaled ds,scf; /* design size and scale factor for a text node */
5393 font_number f; /* for loops over fonts while (un)marking characters */
5394 boolean transformed; /* is the coordinate system being transformed? */
5395 int prologues, procset;
5398 mp->jump_buf = malloc(sizeof(jmp_buf));
5399 if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)))
5402 if (mp->history >= mp_fatal_error_stop ) return 1;
5404 prologues = (int)((unsigned)mp->internal[mp_prologues]>>16);
5406 prologues=qprologues;
5408 procset = (int)((unsigned)mp->internal[mp_procset]>>16);
5411 mp_open_output_file(mp);
5412 mp_print_initial_comment(mp, hh, prologues);
5414 @<Unmark all marked characters@>;
5415 if ( prologues==2 || prologues==3 ) {
5416 mp_reload_encodings(mp);
5418 @<Scan all the text nodes and mark the used characters@>;
5419 if ( prologues==2 || prologues==3 ) {
5420 mp_print_improved_prologue(mp, hh, prologues, procset);
5422 mp_print_prologue(mp, hh, prologues, procset);
5424 mp_gs_unknown_graphics_state(mp, 0);
5427 if ( gr_has_color(p) ) {
5428 @<Write |pre_script| of |p|@>;
5430 mp_gr_fix_graphics_state(mp, p);
5431 switch (gr_type(p)) {
5433 if ( gr_pen_p((mp_fill_object *)p)==NULL ) {
5434 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5435 } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) ) {
5436 mp_gr_stroke_ellipse(mp, p,true);
5438 mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
5439 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5441 if ( gr_post_script((mp_fill_object *)p)!=NULL ) {
5442 mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p));
5446 case mp_stroked_code:
5447 if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) )
5448 mp_gr_stroke_ellipse(mp, p,false);
5450 mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p));
5452 if ( gr_post_script((mp_stroked_object *)p)!=NULL ) {
5453 mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p));
5458 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5460 scf=mp_gr_choose_scale(mp, p);
5462 scf=mp_indexed_size(mp, gr_font_n(p), (quarterword)gr_size_index(p));
5463 @<Shift or transform as necessary before outputting text node~|p| at scale
5464 factor~|scf|; set |transformed:=true| if the original transformation must
5466 mp_ps_string_out(mp, gr_text_p(p));
5467 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5468 @<Print the size information and \ps\ commands for text node~|p|@>;
5471 if ( gr_post_script((mp_text_object *)p)!=NULL ) {
5472 mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp);
5475 case mp_start_clip_code:
5476 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5477 mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p));
5478 mp_ps_print_cmd(mp, " clip"," W");
5480 if ( mp->internal[mp_restore_clip_color]>0 )
5481 mp_gs_unknown_graphics_state(mp, 1);
5483 case mp_stop_clip_code:
5484 mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5486 if ( mp->internal[mp_restore_clip_color]>0 )
5487 mp_gs_unknown_graphics_state(mp, 2);
5489 mp_gs_unknown_graphics_state(mp, -1);
5491 case mp_start_bounds_code:
5492 case mp_stop_bounds_code:
5494 case mp_special_code:
5496 mp_special_object *ps = (mp_special_object *)p;
5497 mp_ps_print_nl (mp, gr_pre_script(ps));
5498 mp_ps_print_ln (mp);
5501 } /* all cases are enumerated */
5504 mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
5505 mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
5506 (mp->close_file)(mp,mp->ps_file);
5513 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) ;
5516 int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) {
5517 return mp_gr_ship_out (hh, prologues, procset, (int)true);
5525 @d do_write_prescript(a,b) {
5526 if ( (gr_pre_script((b *)a))!=NULL ) {
5527 mp_ps_print_nl (mp, gr_pre_script((b *)a));
5532 @<Write |pre_script| of |p|@>=
5534 if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); }
5535 else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); }
5536 else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); }
5539 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5541 mp_ps_print_char(mp, ' ');
5542 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5543 mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5544 mp_ps_print(mp, " fshow");
5546 mp_ps_print_cmd(mp, " grestore"," Q")
5550 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5551 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5552 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5553 if ( transformed ) {
5554 mp_ps_print_cmd(mp, "gsave [", "q [");
5555 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5556 mp_make_scaled(mp, gr_tyx_val(p),scf));
5557 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5558 mp_make_scaled(mp, gr_tyy_val(p),scf));
5559 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5560 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5562 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5563 mp_ps_print_cmd(mp, "moveto","m");
5568 void mp_gr_toss_objects ( mp_edge_object *hh) ;
5569 void mp_gr_toss_object (mp_graphic_object *p) ;
5572 void mp_gr_toss_object (mp_graphic_object *p) {
5574 mp_stroked_object *ts;
5576 switch (gr_type(p)) {
5578 tf = (mp_fill_object *)p;
5579 mp_xfree(gr_pre_script(tf));
5580 mp_xfree(gr_post_script(tf));
5581 mp_gr_toss_knot_list(mp,gr_pen_p(tf));
5582 mp_gr_toss_knot_list(mp,gr_path_p(tf));
5583 mp_gr_toss_knot_list(mp,gr_htap_p(p));
5585 case mp_stroked_code:
5586 ts = (mp_stroked_object *)p;
5587 mp_xfree(gr_pre_script(ts));
5588 mp_xfree(gr_post_script(ts));
5589 mp_gr_toss_knot_list(mp,gr_pen_p(ts));
5590 mp_gr_toss_knot_list(mp,gr_path_p(ts));
5591 if (gr_dash_p(p)!=NULL)
5592 mp_gr_toss_dashes (mp,gr_dash_p(p));
5595 tt = (mp_text_object *)p;
5596 mp_xfree(gr_pre_script(tt));
5597 mp_xfree(gr_post_script(tt));
5598 mp_xfree(gr_text_p(p));
5599 mp_xfree(gr_font_name(p));
5601 case mp_start_clip_code:
5602 mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p));
5604 case mp_start_bounds_code:
5605 mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p));
5607 case mp_stop_clip_code:
5608 case mp_stop_bounds_code:
5610 case mp_special_code:
5611 mp_xfree(gr_pre_script((mp_special_object *)p));
5613 } /* all cases are enumerated */
5619 void mp_gr_toss_objects (mp_edge_object *hh) {
5620 mp_graphic_object *p, *q;
5624 mp_gr_toss_object(p);
5631 mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ;
5635 mp_gr_copy_object (MP mp, mp_graphic_object *p) {
5637 mp_stroked_object *ts;
5640 mp_bounds_object *tb;
5641 mp_special_object *tp;
5642 mp_graphic_object *q = NULL;
5643 switch (gr_type(p)) {
5645 tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code);
5646 gr_pre_script(tf) = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p));
5647 gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p));
5648 gr_path_p(tf) = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p));
5649 gr_htap_p(tf) = mp_gr_copy_path(mp,gr_htap_p(p));
5650 gr_pen_p(tf) = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p));
5651 q = (mp_graphic_object *)tf;
5653 case mp_stroked_code:
5654 ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code);
5655 gr_pre_script(ts) = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p));
5656 gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p));
5657 gr_path_p(ts) = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p));
5658 gr_pen_p(ts) = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p));
5659 gr_dash_p(ts) = mp_gr_copy_dashes(mp,gr_dash_p(p));
5660 q = (mp_graphic_object *)ts;
5663 tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code);
5664 gr_pre_script(tt) = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p));
5665 gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p));
5666 gr_text_p(tt) = mp_xstrdup(mp, gr_text_p(p));
5667 gr_font_name(tt) = mp_xstrdup(mp, gr_font_name(p));
5668 q = (mp_graphic_object *)tt;
5670 case mp_start_clip_code:
5671 tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code);
5672 gr_path_p(tc) = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p));
5673 q = (mp_graphic_object *)tc;
5675 case mp_start_bounds_code:
5676 tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code);
5677 gr_path_p(tb) = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p));
5678 q = (mp_graphic_object *)tb;
5680 case mp_special_code:
5681 tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code);
5682 gr_pre_script(tp) = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p));
5683 q = (mp_graphic_object *)tp;
5685 case mp_stop_clip_code:
5686 q = mp_new_graphic_object(mp, mp_stop_clip_code);
5688 case mp_stop_bounds_code:
5689 q = mp_new_graphic_object(mp, mp_stop_bounds_code);
5691 } /* all cases are enumerated */