1 % $Id: mp.web,v 1.8 2005/08/24 10:54:02 taco Exp $
2 % MetaPost, by John Hobby. Public domain.
4 % Much of this program was copied with permission from MF.web Version 1.9
5 % It interprets a language very similar to D.E. Knuth's METAFONT, but with
6 % changes designed to make it more suitable for PostScript output.
8 % TeX is a trademark of the American Mathematical Society.
9 % METAFONT is a trademark of Addison-Wesley Publishing Company.
10 % PostScript is a trademark of Adobe Systems Incorporated.
12 % Here is TeX material that gets inserted after \input webmac
13 \def\hang{\hangindent 3em\noindent\ignorespaces}
14 \def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
17 \def\ph{\hbox{Pascal-H}}
18 \def\psqrt#1{\sqrt{\mathstrut#1}}
20 \def\pct!{{\char`\%}} % percent sign in ordinary text
21 \font\tenlogo=logo10 % font used for the METAFONT logo
23 \def\MF{{\tenlogo META}\-{\tenlogo FONT}}
24 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
25 \def\<#1>{$\langle#1\rangle$}
26 \def\section{\mathhexbox278}
27 \let\swap=\leftrightarrow
28 \def\round{\mathop{\rm round}\nolimits}
29 \mathchardef\vb="026A % synonym for `\|'
30 \def\[#1]{} % from pascal web
31 \def\(#1){} % this is used to make section names sort themselves better
32 \def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
34 \let\?=\relax % we want to be able to \write a \?
36 \def\title{MetaPost \ps\ output}
37 \def\topofcontents{\hsize 5.5in
38 \vglue -30pt plus 1fil minus 1.5in
39 \def\?##1]{\hbox to 1in{\hfil##1.\ }}
41 \def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
50 @d unity 0200000 /* $2^{16}$, represents 1.00000 */
51 @d el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
52 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
53 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
54 @d negate(A) (A)=-(A) /* change the sign of a variable */
57 @d print_err(A) mp_print_err(mp,(A))
67 #include "mpmp.h" /* internal header */
68 #include "mppsout.h" /* internal header */
71 @<Static variables in the outer block@>;
73 @ There is a small bit of code from the backend that bleads through
74 to the frontend because I do not know how to set up the includes
75 properly. Those are the definitions of |struct libavl_allocator|
76 and |typedef struct psout_data_struct * psout_data|.
78 The |libavl_allocator| is a trick that makes sure that frontends
79 do not need |avl.h|, and the |psout_data| is needed for the backend
84 typedef struct psout_data_struct {
87 @<Exported function headers@>
89 @ @<Exported function headers@>=
90 void mp_backend_initialize (MP mp) ;
91 void mp_backend_free (MP mp) ;
94 @c void mp_backend_initialize (MP mp) {
95 mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
96 @<Set initial values@>;
98 void mp_backend_free (MP mp) {
99 @<Dealloc variables@>;
108 @* Traditional {psfonts.map} loading.
110 TODO: It is likely that this code can be removed after a few minor tweaks.
112 @ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
113 PostScript names for fonts that do not have to be downloaded, i.e., fonts that
114 can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
115 one or more spaces, a PostScript name, and possibly a space and some other junk.
116 This routine reads the table, updates |font_ps_name| entries starting after
117 |last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|. If the file |ps_tab_file|
118 is missing, we assume that the existing font names are OK and nothing needs to
121 @d ps_tab_name "psfonts.map" /* locates font name translation table */
124 static void mp_read_psname_table (MP mp) ;
126 @ @c static void mp_read_psname_table (MP mp) {
127 font_number k; /* font for possible name match */
128 unsigned int lmax; /* upper limit on length of name to match */
129 unsigned int j; /* characters left to read before string gets too long */
130 char *s; /* possible font name to match */
131 text_char c=0; /* character being read from |ps_tab_file| */
132 if ( (mp->ps->ps_tab_file = mp_open_file(mp, ps_tab_name, "r", mp_filetype_fontmap)) ) {
133 @<Set |lmax| to the maximum |font_name| length for fonts
134 |last_ps_fnum+1| through |last_fnum|@>;
135 while (! feof(mp->ps->ps_tab_file) ) {
136 @<Read at most |lmax| characters from |ps_tab_file| into string |s|
137 but |goto common_ending| if there is trouble@>;
138 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
139 if ( mp_xstrcmp(s,mp->font_name[k])==0 ) {
140 @<|flush_string(s)|, read in |font_ps_name[k]|, and
141 |goto common_ending|@>;
146 c = fgetc(mp->ps->ps_tab_file);
148 c = fgetc(mp->ps->ps_tab_file);
150 ungetc(c,mp->ps->ps_tab_file);
153 mp->last_ps_fnum=mp->last_fnum;
154 fclose(mp->ps->ps_tab_file);
159 FILE * ps_tab_file; /* file for font name translation table */
161 @ @<Set |lmax| to the maximum |font_name| length for fonts...@>=
163 for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
164 if (strlen(mp->font_name[k])>lmax )
165 lmax=strlen(mp->font_name[k]);
168 @ If we encounter the end of line before we have started reading
169 characters from |ps_tab_file|, we have found an entirely blank
170 line and we skip over it. Otherwise, we abort if the line ends
171 prematurely. If we encounter a comment character, we also skip
172 over the line, since recent versions of \.{dvips} allow comments
173 in the font map file.
175 TODO: this is probably not safe in the case of a really
176 broken font map file.
178 @<Read at most |lmax| characters from |ps_tab_file| into string |s|...@>=
179 s=mp_xmalloc(mp,lmax+1,1);
182 if (c == '\n' || c == '\r' ) {
184 mp_xfree(s); s=NULL; goto COMMON_ENDING;
186 mp_fatal_error(mp, "The psfont map file is bad!");
189 c = fgetc(mp->ps->ps_tab_file);
190 if (c=='%' || c=='*' || c==';' || c=='#' ) {
191 mp_xfree(s); s=NULL; goto COMMON_ENDING;
193 if (c==' ' || c=='\t') break;
195 s[j++] = mp->xord[c];
197 mp_xfree(s); s=NULL; goto COMMON_ENDING;
202 @ PostScript font names should be at most 28 characters long but we allow 32
205 @<|flush_string(s)|, read in |font_ps_name[k]|, and...@>=
210 if (c=='\n' || c == '\r')
211 mp_fatal_error(mp, "The psfont map file is bad!");
212 c = fgetc(mp->ps->ps_tab_file);
213 } while (c==' ' || c=='\t');
214 ps_name = mp_xmalloc(mp,33,1);
218 mp_fatal_error(mp, "The psfont map file is bad!");
220 ps_name[j++] = mp->xord[c];
221 if (c=='\n' || c == '\r')
224 c = fgetc(mp->ps->ps_tab_file);
225 } while (c != ' ' && c != '\t');
227 mp_xfree(mp->font_ps_name[k]);
228 mp->font_ps_name[k]=ps_name;
234 @* \[44a] Dealing with font encodings.
236 First, here are a few helpers for parsing files
238 @d check_buf(size, buf_size)
239 if ((unsigned)(size) > (unsigned)(buf_size)) {
241 snprintf(s,128,"buffer overflow: (%d,%d) at file %s, line %d",
242 size,buf_size, __FILE__, __LINE__ );
243 mp_fatal_error(mp,s);
246 @d append_char_to_buf(c, p, buf, buf_size) do {
249 if (c == 13 || c == EOF)
251 if (c != ' ' || (p > buf && p[-1] != 32)) {
252 check_buf(p - buf + 1, (buf_size));
257 @d append_eol(p, buf, buf_size) do {
258 check_buf(p - buf + 2, (buf_size));
259 if (p - buf > 1 && p[-1] != 10)
261 if (p - buf > 2 && p[-2] == 32) {
268 @d remove_eol(p, buf) do {
274 @d skip(p, c) if (*p == c) p++
275 @d strend(s) strchr(s, 0)
276 @d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
281 boolean loaded; /* the encoding has been loaded? */
282 char *file_name; /* encoding file name */
283 char *enc_name; /* encoding true name */
284 integer objnum; /* object number */
286 integer tounicode; /* object number of associated ToUnicode entry */
296 #define ENC_BUF_SIZE 0x1000
297 char enc_line[ENC_BUF_SIZE];
301 @d enc_getchar() getc(mp->ps->enc_file)
302 @d enc_eof() feof(mp->ps->enc_file)
303 @d enc_close() fclose(mp->ps->enc_file)
306 static boolean mp_enc_open (MP mp, char *n) {
307 mp->ps->enc_file=mp_open_file(mp, n, "rb", mp_filetype_encoding);
308 if (mp->ps->enc_file!=NULL)
313 static void mp_enc_getline (MP mp) {
318 print_err("unexpected end of file");
321 p = mp->ps->enc_line;
324 append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
326 append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
327 if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
330 static void mp_load_enc (MP mp, char *enc_name,
331 char **enc_encname, char **glyph_names){
332 char buf[ENC_BUF_SIZE], *p, *r;
335 int save_selector = mp->selector;
336 if (!mp_enc_open (mp,enc_name)) {
337 mp_print (mp,"cannot open encoding file for reading");
340 mp_normalize_selector(mp);
342 mp_print (mp, enc_name);
344 if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
345 remove_eol (r, mp->ps->enc_line);
346 print_err ("invalid encoding vector (a name or `[' missing): `");
347 mp_print(mp,mp->ps->enc_line);
351 while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
352 myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
353 memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
354 *(myname+(r-mp->ps->enc_line-1))=0;
355 *enc_encname = myname;
363 *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
366 if (names_count > 256) {
367 print_err ("encoding vector contains more than 256 names");
370 if (mp_xstrcmp (buf, notdef) != 0)
371 glyph_names[names_count] = mp_xstrdup (mp,buf);
374 if (*r != 10 && *r != '%') {
375 if (str_prefix (r, "] def"))
378 remove_eol (r, mp->ps->enc_line);
380 ("invalid encoding vector: a name or `] def' expected: `");
381 mp_print(mp,mp->ps->enc_line);
387 r = mp->ps->enc_line;
392 mp->selector = save_selector;
394 static void mp_read_enc (MP mp, enc_entry * e) {
398 mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
402 @ |write_enc| is used to write either external encoding (given in map file) or
403 internal encoding (read from the font file); when |glyph_names| is NULL
404 the 2nd argument is a pointer to the encoding entry; otherwise the 3rd is
405 the object number of the Encoding object
408 static void mp_write_enc (MP mp, char **glyph_names, enc_entry * e) {
413 if (glyph_names == NULL) {
414 if (e->objnum != 0) /* the encoding has been written already */
422 mp_print(mp,"\n%%%%BeginResource: encoding ");
423 mp_print(mp, e->enc_name);
425 mp_print(mp, e->enc_name);
427 foffset = strlen(e->file_name)+3;
428 for (i = 0; i < 256; i++) {
430 if (s+1+foffset>=80) {
435 mp_print_char(mp,'/');
437 mp_print_char(mp,' ');
441 mp_print_nl (mp,"] def\n");
442 mp_print(mp,"%%%%EndResource");
446 @ All encoding entries go into AVL tree for fast search by name.
449 struct avl_table *enc_tree;
451 @ Memory management functions for avl
453 @<Static variables in the outer block@>=
454 static const char notdef[] = ".notdef";
457 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size);
458 static void avl_xfree (struct libavl_allocator *allocator, void *block);
461 static void *avl_xmalloc (struct libavl_allocator *allocator, size_t size) {
463 return malloc (size);
465 static void avl_xfree (struct libavl_allocator *allocator, void *block) {
471 struct libavl_allocator avl_xallocator;
473 @ @<Set initial...@>=
474 mp->ps->avl_xallocator.libavl_malloc=avl_xmalloc;
475 mp->ps->avl_xallocator.libavl_free= avl_xfree;
476 mp->ps->enc_tree = NULL;
479 static int comp_enc_entry (const void *pa, const void *pb, void *p) {
481 return strcmp (((const enc_entry *) pa)->file_name,
482 ((const enc_entry *) pb)->file_name);
484 static enc_entry * mp_add_enc (MP mp, char *s) {
488 if (mp->ps->enc_tree == NULL) {
489 mp->ps->enc_tree = avl_create (comp_enc_entry, NULL, &mp->ps->avl_xallocator);
492 p = (enc_entry *) avl_find (mp->ps->enc_tree, &tmp);
493 if (p != NULL) /* encoding already registered */
495 p = mp_xmalloc (mp,1,sizeof (enc_entry));
497 p->file_name = mp_xstrdup (mp,s);
500 p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
501 for (i = 0; i < 256; i++)
502 p->glyph_names[i] = (char *) notdef;
503 aa = avl_probe (mp->ps->enc_tree, p);
510 static void mp_destroy_enc_entry (void *pa, void *pb) {
514 p = (enc_entry *) pa;
516 mp_xfree (p->file_name);
517 if (p->glyph_names != NULL)
518 for (i = 0; i < 256; i++)
519 if (p->glyph_names[i] != notdef)
520 mp_xfree (p->glyph_names[i]);
521 mp_xfree (p->glyph_names);
526 static void enc_free (MP mp);
528 @ @c static void enc_free (MP mp) {
529 if (mp->ps->enc_tree != NULL)
530 avl_destroy (mp->ps->enc_tree, mp_destroy_enc_entry);
533 @ @<Exported function headers@>=
534 void mp_reload_encodings (MP mp) ;
537 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) ;
539 @ @c void mp_reload_encodings (MP mp) {
543 int lastfnum = mp->last_fnum;
544 for (f=null_font+1;f<=lastfnum;f++) {
545 if (mp->font_enc_name[f]!=NULL ) {
546 mp_xfree(mp->font_enc_name[f]);
547 mp->font_enc_name[f]=NULL;
549 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f,&fm_cur)) {
550 if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
551 e = fm_cur->encoding;
557 static void mp_font_encodings (MP mp, int lastfnum, int encodings_only) {
561 for (f=null_font+1;f<=lastfnum;f++) {
562 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
563 if (fm != NULL && (fm->ps_name != NULL)) {
564 if (is_reencoded (fm)) {
565 if (encodings_only || (!is_subsetted (fm))) {
567 mp_write_enc (mp,NULL, e);
568 /* clear for next run */
577 @* \[44b] Parsing font map files.
585 @d fm_close() fclose(mp->ps->fm_file)
586 @d fm_getchar() fgetc(mp->ps->fm_file)
587 @d fm_eof() feof(mp->ps->fm_file)
590 enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
591 enum _ltype { MAPFILE, MAPLINE };
592 enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
593 typedef struct mitem {
594 int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
595 int type; /* map file or map line */
596 char *map_line; /* pointer to map file name or map line */
597 int lineno; /* line number in map file */
603 fm_entry *loaded_tfm_found;
604 fm_entry *avail_tfm_found;
605 fm_entry *non_tfm_found;
606 fm_entry *not_avail_tfm_found;
608 @ @<Set initial...@>=
609 mp->ps->mitem = NULL;
612 static const char nontfm[] = "<nontfm>";
615 @d read_field(r, q, buf) do {
617 while (*r != ' ' && *r != '\0')
625 fm->F = mp_xstrdup(mp,buf);
637 static fm_entry *new_fm_entry (MP mp) {
639 fm = mp_xmalloc (mp,1,sizeof(fm_entry));
644 fm->subset_tag = NULL;
646 fm->tfm_num = null_font;
647 fm->tfm_avail = TFM_UNCHECKED;
655 fm->all_glyphs = false;
662 static void delete_fm_entry (fm_entry * fm) {
663 mp_xfree (fm->tfm_name);
664 mp_xfree (fm->ps_name);
665 mp_xfree (fm->ff_name);
666 mp_xfree (fm->subset_tag);
667 mp_xfree (fm->charset);
671 static ff_entry *new_ff_entry (MP mp) {
673 ff = mp_xmalloc (mp,1,sizeof(ff_entry));
679 static void delete_ff_entry (ff_entry * ff) {
680 mp_xfree (ff->ff_name);
681 mp_xfree (ff->ff_path);
685 static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
686 static char buf[SMALL_BUF_SIZE];
687 char *p = tfmname, *r = strend (p) - 1, *q = r;
688 while (q > p && isdigit (*q))
690 if (!(q > p) || q == r || (*q != '+' && *q != '-'))
692 check_buf (q - p + 1, SMALL_BUF_SIZE);
693 strncpy (buf, p, (size_t) (q - p));
699 @ @<Exported function headers@>=
700 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
703 boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
704 fm_entry *res = NULL;
705 res = mp_fm_lookup (mp, f);
709 return (res != NULL);
713 struct avl_table *tfm_tree;
714 struct avl_table *ps_tree;
715 struct avl_table *ff_tree;
717 @ @<Set initial...@>=
718 mp->ps->tfm_tree = NULL;
719 mp->ps->ps_tree = NULL;
720 mp->ps->ff_tree = NULL;
722 @ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
725 static int comp_fm_entry_tfm (const void *pa, const void *pb, void *p) {
727 return strcmp (((const fm_entry *) pa)->tfm_name,
728 ((const fm_entry *) pb)->tfm_name);
731 @ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
733 @c static int comp_fm_entry_ps (const void *pa, const void *pb, void *p) {
735 const fm_entry *p1 = (const fm_entry *) pa, *p2 = (const fm_entry *) pb;
737 assert (p1->ps_name != NULL && p2->ps_name != NULL);
738 if ((i = strcmp (p1->ps_name, p2->ps_name)))
740 cmp_return (p1->slant, p2->slant);
741 cmp_return (p1->extend, p2->extend);
742 if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
743 (i = strcmp (p1->tfm_name, p2->tfm_name)))
748 @ AVL sort |ff_entry| into |ff_tree| by |ff_name|
750 @c static int comp_ff_entry (const void *pa, const void *pb, void *p) {
752 return strcmp (((const ff_entry *) pa)->ff_name,
753 ((const ff_entry *) pb)->ff_name);
756 @ @c static void create_avl_trees (MP mp) {
757 if (mp->ps->tfm_tree == NULL) {
758 mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm, NULL, &mp->ps->avl_xallocator);
759 assert (mp->ps->tfm_tree != NULL);
761 if (mp->ps->ps_tree == NULL) {
762 mp->ps->ps_tree = avl_create (comp_fm_entry_ps, NULL, &mp->ps->avl_xallocator);
763 assert (mp->ps->ps_tree != NULL);
765 if (mp->ps->ff_tree == NULL) {
766 mp->ps->ff_tree = avl_create (comp_ff_entry, NULL, &mp->ps->avl_xallocator);
767 assert (mp->ps->ff_tree != NULL);
771 @ The function |avl_do_entry| is not completely symmetrical with regards
772 to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
773 |goto exit|, and no |ps_name| link is tried. This is to keep it compatible
774 with the original version.
778 @d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
779 @d set_pslink(fm) ((fm)->links |= LINK_PS)
780 @d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
781 @d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
782 @d has_tfmlink(fm) ((fm)->links & LINK_TFM)
783 @d has_pslink(fm) ((fm)->links & LINK_PS)
786 static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
792 /* handle |tfm_name| link */
794 if (strcmp (fp->tfm_name, nontfm)) {
795 p = (fm_entry *) avl_find (mp->ps->tfm_tree, fp);
797 if (mode == FM_DUPIGNORE) {
798 snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
802 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
803 if (mp_has_font_size(mp,p->tfm_num)) {
805 "fontmap entry for `%s' has been used, replace/delete not allowed",
810 a = avl_delete (mp->ps->tfm_tree, p);
817 if (mode != FM_DELETE) {
818 aa = avl_probe (mp->ps->tfm_tree, fp);
824 /* handle |ps_name| link */
826 if (fp->ps_name != NULL) {
827 assert (fp->tfm_name != NULL);
828 p = (fm_entry *) avl_find (mp->ps->ps_tree, fp);
830 if (mode == FM_DUPIGNORE) {
832 "ps_name entry for `%s' already exists, duplicates ignored",
836 } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
837 if (mp_has_font_size(mp,p->tfm_num)) {
838 /* REPLACE/DELETE not allowed */
840 "fontmap entry for `%s' has been used, replace/delete not allowed",
845 a = avl_delete (mp->ps->ps_tree, p);
848 if (!has_tfmlink (p))
852 if (mode != FM_DELETE) {
853 aa = avl_probe (mp->ps->ps_tree, fp);
859 if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
860 return 1; /* deallocation of |fm_entry| structure required */
865 @ consistency check for map entry, with warn flag
868 static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
872 if (fm->ps_name != NULL) {
873 if (is_basefont (fm)) {
874 if (is_fontfile (fm) && !is_included (fm)) {
876 snprintf(s,128, "invalid entry for `%s': "
877 "font file must be included or omitted for base fonts",
883 } else { /* not a base font */
884 /* if no font file given, drop this entry */
885 /* |if (!is_fontfile (fm)) {
888 "invalid entry for `%s': font file missing",
897 if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
900 "invalid entry for `%s': only subsetted TrueType font can be reencoded",
906 if ((fm->slant != 0 || fm->extend != 0) &&
907 (is_truetype (fm))) {
910 "invalid entry for `%s': "
911 "SlantFont/ExtendFont can be used only with embedded T1 fonts",
917 if (abs (fm->slant) > 1000) {
920 "invalid entry for `%s': too big value of SlantFont (%g)",
921 fm->tfm_name, fm->slant / 1000.0);
926 if (abs (fm->extend) > 2000) {
929 "invalid entry for `%s': too big value of ExtendFont (%g)",
930 fm->tfm_name, fm->extend / 1000.0);
936 !(is_truetype (fm) && is_included (fm) &&
937 is_subsetted (fm) && !is_reencoded (fm))) {
940 "invalid entry for `%s': "
941 "PidEid can be used only with subsetted non-reencoded TrueType fonts",
950 @ returns true if s is one of the 14 std. font names; speed-trimmed.
952 @c static boolean check_basefont (char *s) {
953 static const char *basefont_names[] = {
955 "Courier-Bold", /* 1:12 */
956 "Courier-Oblique", /* 2:15 */
957 "Courier-BoldOblique", /* 3:19 */
958 "Helvetica", /* 4:9 */
959 "Helvetica-Bold", /* 5:14 */
960 "Helvetica-Oblique", /* 6:17 */
961 "Helvetica-BoldOblique", /* 7:21 */
963 "Times-Roman", /* 9:11 */
964 "Times-Bold", /* 10:10 */
965 "Times-Italic", /* 11:12 */
966 "Times-BoldItalic", /* 12:16 */
967 "ZapfDingbats" /* 13:12 */
969 static const int Index[] =
970 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
973 const size_t n = strlen (s);
977 if (n == 12) { /* three names have length 12 */
980 k = 1; /* Courier-Bold */
983 k = 11; /* Times-Italic */
986 k = 13; /* ZapfDingbats */
993 if (k > -1 && !strcmp (basefont_names[k], s))
999 @d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
1001 @c static void fm_scan_line (MP mp) {
1002 int a, b, c, j, u = 0, v = 0;
1005 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
1006 char *p, *q, *r, *s;
1008 switch (mp->ps->mitem->type) {
1013 append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
1020 r = mp->ps->mitem->map_line;
1025 if (*r == '\0' || is_cfg_comment (*r))
1027 fm = new_fm_entry (mp);
1028 read_field (r, q, buf);
1029 set_field (tfm_name);
1031 read_field (r, q, buf);
1032 if (*buf != '<' && *buf != '"')
1033 set_field (ps_name);
1035 r = p; /* unget the field */
1036 if (isdigit (*r)) { /* font flags given */
1037 fm->flags = atoi (r);
1038 while (isdigit (*r))
1041 while (1) { /* loop through "specials", encoding, font file */
1046 case '"': /* opening quote */
1051 if (sscanf (r, "%f %n", &d, &j) > 0) {
1052 s = r + j; /* jump behind number, eat also blanks, if any */
1053 if (*(s - 1) == 'E' || *(s - 1) == 'e')
1054 s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
1055 if (str_prefix (s, "SlantFont")) {
1056 d *= 1000.0; /* correct rounding also for neg. numbers */
1057 fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1058 r = s + strlen ("SlantFont");
1059 } else if (str_prefix (s, "ExtendFont")) {
1061 fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5);
1062 if (fm->extend == 1000)
1064 r = s + strlen ("ExtendFont");
1065 } else { /* unknown name */
1067 *r != ' ' && *r != '"' && *r != '\0';
1068 r++); /* jump over name */
1069 c = *r; /* remember char for temporary end of string */
1071 snprintf(warn_s,128,
1072 "invalid entry for `%s': unknown name `%s' ignored",
1078 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
1081 if (*r == '"') /* closing quote */
1084 snprintf(warn_s,128,
1085 "invalid entry for `%s': closing quote missing",
1091 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
1092 if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
1098 default: /* encoding or font file specification */
1102 if (*r == '<' || *r == '[')
1105 read_field (r, q, buf);
1106 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
1107 if (strlen (buf) > 4 && strcasecmp (strend (buf) - 4, ".enc") == 0) {
1108 fm->encoding = mp_add_enc (mp, buf);
1109 u = v = 0; /* u, v used if intervening blank: "<< foo" */
1110 } else if (strlen (buf) > 0) { /* file name given */
1111 /* font file, formats:
1112 * subsetting: '<cmr10.pfa'
1113 * no subsetting: '<<cmr10.pfa'
1114 * no embedding: 'cmr10.pfa'
1116 if (a == '<' || u == '<') {
1118 if ((a == '<' && b == 0) || (a == 0 && v == 0))
1120 /* otherwise b == '<' (or '[') => no subsetting */
1122 set_field (ff_name);
1131 if (fm->ps_name != NULL && check_basefont (fm->ps_name))
1133 if (is_fontfile (fm)
1134 && strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
1136 if (check_fm_entry (mp,fm, true) != 0)
1139 Until here the map line has been completely scanned without errors;
1140 fm points to a valid, freshly filled-out |fm_entry| structure.
1141 Now follows the actual work of registering/deleting.
1143 if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
1146 delete_fm_entry (fm);
1150 @c static void fm_read_info (MP mp) {
1153 if (mp->ps->tfm_tree == NULL)
1154 create_avl_trees (mp);
1155 if (mp->ps->mitem->map_line == NULL) /* nothing to do */
1157 mp->ps->mitem->lineno = 1;
1158 switch (mp->ps->mitem->type) {
1160 n = mp->ps->mitem->map_line;
1161 mp->ps->fm_file = mp_open_file(mp, n, "r", mp_filetype_fontmap);
1162 if (!mp->ps->fm_file) {
1163 snprintf(s,256,"cannot open font map file %s",n);
1166 int save_selector = mp->selector;
1167 mp_normalize_selector(mp);
1170 while (!fm_eof ()) {
1172 mp->ps->mitem->lineno++;
1176 mp->selector = save_selector;
1177 mp->ps->fm_file = NULL;
1186 mp->ps->mitem->map_line = NULL; /* done with this line */
1191 static scaled mp_round_xn_over_d (MP mp, scaled x, integer n, integer d) {
1192 boolean positive; /* was |x>=0|? */
1193 unsigned int t,u; /* intermediate quantities */
1194 integer v; /* intermediate quantities */
1198 negate(x); positive=false;
1201 u=(x / 0100000)*n+(t / 0100000);
1202 v=(u % d)*0100000 + (t % 0100000);
1203 if ( u / d>=0100000 ) mp->arith_error=true;
1204 else u=0100000*(u / d) + (v / d);
1208 return ( positive ? u : -u );
1210 static fm_entry *mk_ex_fm (MP mp, font_number f, fm_entry * basefm, int ex) {
1212 integer e = basefm->extend;
1215 fm = new_fm_entry (mp);
1216 fm->flags = basefm->flags;
1217 fm->encoding = basefm->encoding;
1218 fm->type = basefm->type;
1219 fm->slant = basefm->slant;
1220 fm->extend = mp_round_xn_over_d (mp, e, 1000 + ex, 1000);
1221 /* modify ExtentFont to simulate expansion */
1222 if (fm->extend == 1000)
1224 fm->tfm_name = mp_xstrdup (mp,mp->font_name[f]);
1225 if (basefm->ps_name != NULL)
1226 fm->ps_name = mp_xstrdup (mp,basefm->ps_name);
1227 fm->ff_name = mp_xstrdup (mp,basefm->ff_name);
1230 fm->tfm_avail = TFM_FOUND;
1231 assert (strcmp (fm->tfm_name, nontfm));
1235 @ @c static void init_fm (fm_entry * fm, font_number f) {
1236 if (fm->tfm_num == null_font ) {
1238 fm->tfm_avail = TFM_FOUND;
1243 static fm_entry * mp_fm_lookup (MP mp, font_number f);
1246 static fm_entry * mp_fm_lookup (MP mp, font_number f) {
1248 fm_entry *fm, *exfm;
1251 if (mp->ps->tfm_tree == NULL)
1252 fm_read_info (mp); /* only to read default map file */
1253 tfm = mp->font_name[f];
1254 assert (strcmp (tfm, nontfm));
1255 /* Look up for full <tfmname>[+-]<expand> */
1257 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1260 return (fm_entry *) fm;
1262 tfm = mk_base_tfm (mp, mp->font_name[f], &e);
1263 if (tfm == NULL) /* not an expanded font, nothing to do */
1267 fm = (fm_entry *) avl_find (mp->ps->tfm_tree, &tmp);
1268 if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
1269 return (fm_entry *) fm; /* font expansion uses the base font */
1270 /* the following code would be obsolete, as would be |mk_ex_fm| */
1271 if (!is_t1fontfile (fm) || !is_included (fm)) {
1274 "font %s cannot be expanded (not an included Type1 font)", tfm);
1278 exfm = mk_ex_fm (mp, f, fm, e); /* copies all fields from fm except tfm name */
1280 ai = avl_do_entry (mp, exfm, FM_DUPIGNORE);
1282 return (fm_entry *) exfm;
1287 @ Early check whether a font file exists. Used e. g. for replacing fonts
1288 of embedded PDF files: Without font file, the font within the embedded
1289 PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
1290 may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
1291 only once per font file name + expansion parameter. This might help
1292 keeping speed, if many PDF pages with same fonts are to be embedded.
1294 The |ff_tree| contains only font files, which are actually needed,
1295 so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
1298 static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
1303 assert (fm->ff_name != NULL);
1304 tmp.ff_name = fm->ff_name;
1305 ff = (ff_entry *) avl_find (mp->ps->ff_tree, &tmp);
1306 if (ff == NULL) { /* not yet in database */
1307 ff = new_ff_entry (mp);
1308 ff->ff_name = mp_xstrdup (mp,fm->ff_name);
1309 ff->ff_path = mp_xstrdup (mp,fm->ff_name);
1310 aa = avl_probe (mp->ps->ff_tree, ff);
1311 assert (aa != NULL);
1316 @ Process map file given by its name or map line contents. Items not
1317 beginning with [+-=] flush default map file, if it has not yet been
1318 read. Leading blanks and blanks immediately following [+-=] are ignored.
1321 @c static void mp_process_map_item (MP mp, char *s, int type) {
1325 s++; /* ignore leading blank */
1327 case '+': /* +mapfile.map, +mapline */
1328 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
1331 case '=': /* =mapfile.map, =mapline */
1332 mode = FM_REPLACE; /* try to replace earlier entry */
1335 case '-': /* -mapfile.map, -mapline */
1336 mode = FM_DELETE; /* try to delete entry */
1340 mode = FM_DUPIGNORE; /* like +, but also: */
1341 mp->ps->mitem->map_line = NULL; /* flush default map file name */
1344 s++; /* ignore blank after [+-=] */
1345 p = s; /* map item starts here */
1347 case MAPFILE: /* remove blank at end */
1348 while (*p != '\0' && *p != ' ')
1352 case MAPLINE: /* blank at end allowed */
1357 if (mp->ps->mitem->map_line != NULL) /* read default map file first */
1359 if (*s != '\0') { /* only if real item to process */
1360 mp->ps->mitem->mode = mode;
1361 mp->ps->mitem->type = type;
1362 mp->ps->mitem->map_line = s;
1367 @ @<Exported function headers@>=
1368 void mp_map_file (MP mp, str_number t);
1369 void mp_map_line (MP mp, str_number t);
1370 void mp_init_map_file (MP mp, int is_troff);
1373 void mp_map_file (MP mp, str_number t) {
1374 char *s = mp_xstrdup(mp,mp_str (mp,t));
1375 mp_process_map_item (mp, s, MAPFILE);
1378 void mp_map_line (MP mp, str_number t) {
1379 char *s = mp_xstrdup(mp,mp_str (mp,t));
1380 mp_process_map_item (mp, s, MAPLINE);
1385 @c void mp_init_map_file (MP mp, int is_troff) {
1387 mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
1388 mp->ps->mitem->mode = FM_DUPIGNORE;
1389 mp->ps->mitem->type = MAPFILE;
1390 mp->ps->mitem->map_line = NULL;
1391 r = (mp->find_file)("mpost.map", "rb", mp_filetype_fontmap);
1394 mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
1397 mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
1399 mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
1404 @ @<Dealloc variables@>=
1405 if (mp->ps->mitem!=NULL) {
1406 mp_xfree(mp->ps->mitem->map_line);
1407 mp_xfree(mp->ps->mitem);
1413 static void destroy_fm_entry_tfm (void *pa, void *pb) {
1416 fm = (fm_entry *) pa;
1417 if (!has_pslink (fm))
1418 delete_fm_entry (fm);
1422 static void destroy_fm_entry_ps (void *pa, void *pb) {
1425 fm = (fm_entry *) pa;
1426 if (!has_tfmlink (fm))
1427 delete_fm_entry (fm);
1431 static void destroy_ff_entry (void *pa, void *pb) {
1434 ff = (ff_entry *) pa;
1435 delete_ff_entry (ff);
1439 static void fm_free (MP mp);
1442 static void fm_free (MP mp) {
1443 if (mp->ps->tfm_tree != NULL)
1444 avl_destroy (mp->ps->tfm_tree, destroy_fm_entry_tfm);
1445 if (mp->ps->ps_tree != NULL)
1446 avl_destroy (mp->ps->ps_tree, destroy_fm_entry_ps);
1447 if (mp->ps->ff_tree != NULL)
1448 avl_destroy (mp->ps->ff_tree, destroy_ff_entry);
1451 @* \[44c] Helper functions for Type1 fonts.
1454 typedef char char_entry;
1455 typedef unsigned char Byte;
1459 char_entry *char_ptr, *char_array;
1461 char *job_id_string;
1463 @ @<Set initial...@>=
1464 mp->ps->char_array = NULL;
1465 mp->ps->job_id_string = NULL;
1468 @d SMALL_ARRAY_SIZE 256
1472 void mp_set_job_id (MP mp) {
1473 char *name_string, *format_string, *s;
1476 if (mp->ps->job_id_string != NULL)
1478 if ( mp->job_name==NULL )
1479 mp->job_name = mp_xstrdup(mp,"mpout");
1480 name_string = mp_xstrdup (mp,mp->job_name);
1481 format_string = mp_xstrdup (mp,mp->mem_ident);
1482 slen = SMALL_BUF_SIZE +
1483 strlen (name_string) +
1484 strlen (format_string);
1485 s = mp_xmalloc (mp,slen, sizeof (char));
1486 i = snprintf (s, slen,
1487 "%.4d/%.2d/%.2d %.2d:%.2d %s %s",
1488 (mp->internal[mp_year]>>16),
1489 (mp->internal[mp_month]>>16),
1490 (mp->internal[mp_day]>>16),
1491 (mp->internal[mp_time]>>16) / 60,
1492 (mp->internal[mp_time]>>16) % 60,
1493 name_string, format_string);
1494 mp->ps->job_id_string = mp_xstrdup (mp,s);
1496 mp_xfree (name_string);
1497 mp_xfree (format_string);
1499 static void fnstr_append (MP mp, const char *s) {
1500 size_t l = strlen (s) + 1;
1501 alloc_array (char, l, SMALL_ARRAY_SIZE);
1502 strcat (mp->ps->char_ptr, s);
1503 mp->ps->char_ptr = strend (mp->ps->char_ptr);
1506 @ @<Exported function headers@>=
1507 void mp_set_job_id (MP mp) ;
1509 @ @<Dealloc variables@>=
1510 mp_xfree(mp->ps->job_id_string);
1512 @ this is not really a true crc32, but it should be just enough to keep
1513 subsets prefixes somewhat disjunct
1516 static unsigned long crc32 (int oldcrc, const Byte *buf, int len) {
1517 unsigned long ret = 0;
1520 ret = (23<<24)+(45<<16)+(67<<8)+89;
1523 ret = (ret<<2)+buf[i];
1526 static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
1527 integer b; /* |char_base[f]| */
1529 if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
1535 static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, int tex_font)
1541 if (mp->ps->job_id_string ==NULL)
1542 mp_fatal_error(mp, "no job id!");
1543 l = strlen (mp->ps->job_id_string) + 1;
1545 alloc_array (char, l, SMALL_ARRAY_SIZE);
1546 strcpy (mp->ps->char_array, mp->ps->job_id_string);
1547 mp->ps->char_ptr = strend (mp->ps->char_array);
1548 if (fm_cur->tfm_name != NULL) {
1549 fnstr_append (mp," TFM name: ");
1550 fnstr_append (mp,fm_cur->tfm_name);
1552 fnstr_append (mp," PS name: ");
1553 if (fm_cur->ps_name != NULL)
1554 fnstr_append (mp,fm_cur->ps_name);
1555 fnstr_append (mp," Encoding: ");
1556 if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
1557 fnstr_append (mp,(fm_cur->encoding)->file_name);
1559 fnstr_append (mp,"built-in");
1560 fnstr_append (mp," CharSet: ");
1561 for (i = 0; i < 256; i++)
1562 if (mp_char_marked (mp,tex_font, i) && glyph_names[i] != notdef) {
1563 if (glyph_names[i]!=NULL) {
1564 fnstr_append (mp,"/");
1565 fnstr_append (mp,glyph_names[i]);
1568 if (fm_cur->charset != NULL) {
1569 fnstr_append (mp," Extra CharSet: ");
1570 fnstr_append (mp, fm_cur->charset);
1572 crc = crc32 (0L, Z_NULL, 0);
1573 crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
1574 /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
1575 * there are 26 uppercase chars ==> each char represents a number in range
1576 * |0..25|. The maximal number that can be represented by the tag is
1577 * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
1578 * of the CRC must be dropped out.
1580 for (i = 0; i < 6; i++) {
1581 tag[i] = 'A' + crc % 26;
1585 fm_cur->subset_tag = mp_xstrdup (mp,tag);
1591 @d external_enc() (fm_cur->encoding)->glyph_names
1592 @d is_used_char(c) mp_char_marked (mp, tex_font, c)
1593 @d end_last_eexec_line()
1594 mp->ps->hexline_length = HEXLINE_WIDTH;
1596 mp->ps->t1_eexec_encrypt = false
1597 @d t1_log(s) mp_print(mp,(char *)s)
1598 @d t1_putchar(c) fputc(c, mp->ps_file)
1599 @d embed_all_glyphs(tex_font) false
1601 @d extra_charset() mp->ps->dvips_extra_charset
1602 @d update_subset_tag()
1603 @d fixedcontent true
1606 #define PRINTF_BUF_SIZE 1024
1607 char *dvips_extra_charset;
1609 unsigned char *grid;
1610 char *ext_glyph_names[256];
1611 char print_buf[PRINTF_BUF_SIZE];
1613 @ @<Set initial ...@>=
1614 mp->ps->dvips_extra_charset=NULL;
1617 @d t1_getchar() fgetc(mp->ps->t1_file)
1618 @d t1_ungetchar(c) ungetc(c, mp->ps->t1_file)
1619 @d t1_eof() feof(mp->ps->t1_file)
1620 @d t1_close() fclose(mp->ps->t1_file)
1621 @d valid_code(c) (c >= 0 && c < 256)
1623 @<Static variables in the outer block@>=
1624 static const char *standard_glyph_names[256] =
1625 { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1626 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1627 notdef, notdef, notdef, notdef, notdef, notdef,
1628 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1629 "space", "exclam", "quotedbl", "numbersign",
1630 "dollar", "percent", "ampersand", "quoteright", "parenleft",
1631 "parenright", "asterisk", "plus", "comma", "hyphen", "period",
1632 "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
1633 "eight", "nine", "colon", "semicolon", "less",
1634 "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
1635 "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
1636 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
1637 "backslash", "bracketright", "asciicircum", "underscore",
1638 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
1639 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
1640 "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
1641 notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1642 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1643 notdef, notdef, notdef, notdef, notdef, notdef,
1644 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1645 notdef, notdef, notdef, "exclamdown", "cent",
1646 "sterling", "fraction", "yen", "florin", "section", "currency",
1647 "quotesingle", "quotedblleft", "guillemotleft",
1648 "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
1649 "dagger", "daggerdbl", "periodcentered", notdef,
1650 "paragraph", "bullet", "quotesinglbase", "quotedblbase",
1651 "quotedblright", "guillemotright", "ellipsis", "perthousand",
1652 notdef, "questiondown", notdef, "grave", "acute", "circumflex",
1653 "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
1654 "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
1655 notdef, notdef, notdef, notdef, notdef, notdef,
1656 notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
1657 notdef, "AE", notdef, "ordfeminine", notdef, notdef,
1658 notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
1659 notdef, notdef, notdef, notdef, "ae", notdef, notdef,
1660 notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
1661 "germandbls", notdef, notdef, notdef, notdef };
1662 static const char charstringname[] = "/CharStrings";
1665 char **t1_glyph_names;
1666 char *t1_builtin_glyph_names[256];
1667 char charsetstr[0x4000];
1668 boolean read_encoding_only;
1672 #define T1_BUF_SIZE 0x10
1676 #define CS_VMOVETO 4
1677 #define CS_RLINETO 5
1678 #define CS_HLINETO 6
1679 #define CS_VLINETO 7
1680 #define CS_RRCURVETO 8
1681 #define CS_CLOSEPATH 9
1682 #define CS_CALLSUBR 10
1683 #define CS_RETURN 11
1684 #define CS_ESCAPE 12
1686 #define CS_ENDCHAR 14
1687 #define CS_RMOVETO 21
1688 #define CS_HMOVETO 22
1689 #define CS_VHCURVETO 30
1690 #define CS_HVCURVETO 31
1691 #define CS_1BYTE_MAX (CS_HVCURVETO + 1)
1693 #define CS_DOTSECTION CS_1BYTE_MAX + 0
1694 #define CS_VSTEM3 CS_1BYTE_MAX + 1
1695 #define CS_HSTEM3 CS_1BYTE_MAX + 2
1696 #define CS_SEAC CS_1BYTE_MAX + 6
1697 #define CS_SBW CS_1BYTE_MAX + 7
1698 #define CS_DIV CS_1BYTE_MAX + 12
1699 #define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
1700 #define CS_POP CS_1BYTE_MAX + 17
1701 #define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
1702 #define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
1703 #define CS_MAX CS_2BYTE_MAX
1706 typedef unsigned char byte;
1708 byte nargs; /* number of arguments */
1709 boolean bottom; /* take arguments from bottom of stack? */
1710 boolean clear; /* clear stack? */
1712 } cc_entry; /* CharString Command */
1714 char *glyph_name; /* glyph name (or notdef for Subrs entry) */
1716 unsigned short len; /* length of the whole string */
1717 unsigned short cslen; /* length of the encoded part of the string */
1723 unsigned short t1_dr, t1_er;
1724 unsigned short t1_c1, t1_c2;
1725 unsigned short t1_cslen;
1728 @ @<Set initial...@>=
1729 mp->ps->t1_c1 = 52845;
1730 mp->ps->t1_c2 = 22719;
1733 typedef char t1_line_entry;
1734 typedef char t1_buf_entry;
1737 t1_line_entry *t1_line_ptr, *t1_line_array;
1738 size_t t1_line_limit;
1739 t1_buf_entry *t1_buf_ptr, *t1_buf_array;
1740 size_t t1_buf_limit;
1742 cs_entry *cs_tab, *cs_ptr, *cs_notdef;
1743 char *cs_dict_start, *cs_dict_end;
1744 int cs_count, cs_size, cs_size_pos;
1746 char *subr_array_start, *subr_array_end;
1747 int subr_max, subr_size, subr_size_pos;
1749 @ @<Set initial...@>=
1750 mp->ps->t1_line_array = NULL;
1751 mp->ps->t1_buf_array = NULL;
1754 This list contains the begin/end tokens commonly used in the
1755 /Subrs array of a Type 1 font.
1757 @<Static variables in the outer block@>=
1758 static const char *cs_token_pairs_list[][2] = {
1761 {" RD", "noaccess put"},
1762 {" -|", "noaccess put"},
1767 const char **cs_token_pair;
1768 boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
1769 int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
1770 long t1_block_length;
1778 @<Set initial ...@>=
1779 mp->ps->hexline_length = HEXLINE_WIDTH;
1782 @d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
1783 @d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
1784 @d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
1785 @d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
1786 @d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
1787 @d t1_subrs() t1_prefix("/Subrs")
1788 @d t1_end_eexec() t1_suffix("mark currentfile closefile")
1789 @d t1_cleartomark() t1_prefix("cleartomark")
1791 @d isdigit(A) ((A)>='0'&&(A)<='9')
1794 static void end_hexline (MP mp) {
1795 if (mp->ps->hexline_length == HEXLINE_WIDTH) {
1796 fputs ("\n", mp->ps_file);
1797 mp->ps->hexline_length = 0;
1800 static void t1_check_pfa (MP mp) {
1801 const int c = t1_getchar ();
1802 mp->ps->t1_pfa = (c != 128) ? true : false;
1805 static int t1_getbyte (MP mp)
1807 int c = t1_getchar ();
1810 if (mp->ps->t1_block_length == 0) {
1812 mp_fatal_error (mp, "invalid marker");
1819 mp->ps->t1_block_length = t1_getchar () & 0xff;
1820 mp->ps->t1_block_length |= (t1_getchar () & 0xff) << 8;
1821 mp->ps->t1_block_length |= (t1_getchar () & 0xff) << 16;
1822 mp->ps->t1_block_length |= (t1_getchar () & 0xff) << 24;
1825 mp->ps->t1_block_length--;
1828 static int hexval (int c) {
1829 if (c >= 'A' && c <= 'F')
1830 return c - 'A' + 10;
1831 else if (c >= 'a' && c <= 'f')
1832 return c - 'a' + 10;
1833 else if (c >= '0' && c <= '9')
1838 static byte edecrypt (MP mp, byte cipher) {
1840 if (mp->ps->t1_pfa) {
1841 while (cipher == 10 || cipher == 13)
1842 cipher = t1_getbyte (mp);
1843 mp->ps->last_hexbyte = cipher = (hexval (cipher) << 4) + hexval (t1_getbyte (mp));
1845 plain = (cipher ^ (mp->ps->t1_dr >> 8));
1846 mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1849 static byte cdecrypt (MP mp, byte cipher, unsigned short *cr)
1851 const byte plain = (cipher ^ (*cr >> 8));
1852 *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1855 static byte eencrypt (MP mp, byte plain)
1857 const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
1858 mp->ps->t1_er = (cipher + mp->ps->t1_er) * mp->ps->t1_c1 + mp->ps->t1_c2;
1862 static byte cencrypt (MP mp, byte plain, unsigned short *cr)
1864 const byte cipher = (plain ^ (*cr >> 8));
1865 *cr = (cipher + *cr) * mp->ps->t1_c1 + mp->ps->t1_c2;
1869 static char *eol (char *s) {
1870 char *p = strend (s);
1871 if (p - s > 1 && p[-1] != 10) {
1877 static float t1_scan_num (MP mp, char *p, char **r)
1882 if (sscanf (p, "%g", &f) != 1) {
1883 remove_eol (p, mp->ps->t1_line_array);
1884 snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
1885 mp_fatal_error(mp,s);
1888 for (; isdigit (*p) || *p == '.' ||
1889 *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
1895 static boolean str_suffix (const char *begin_buf, const char *end_buf,
1898 const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
1901 while (s1 >= begin_buf && s2 >= s) {
1910 @d alloc_array(T, n, s) do {
1911 if (mp->ps->T##_array == NULL) {
1912 mp->ps->T##_limit = (s);
1913 if ((unsigned)(n) > mp->ps->T##_limit)
1914 mp->ps->T##_limit = (n);
1915 mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
1916 mp->ps->T##_ptr = mp->ps->T##_array;
1918 else if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
1919 size_t last_ptr_index;
1920 last_ptr_index = mp->ps->T##_ptr - mp->ps->T##_array;
1921 mp->ps->T##_limit *= 2;
1922 if ((unsigned)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
1923 mp->ps->T##_limit = mp->ps->T##_ptr - mp->ps->T##_array + (n);
1924 mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
1925 mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
1929 @d out_eexec_char(A) t1_outhex(mp,(A))
1932 static void t1_outhex (MP mp, byte b)
1934 static char *hexdigits = "0123456789ABCDEF";
1935 t1_putchar (hexdigits[b / 16]);
1936 t1_putchar (hexdigits[b % 16]);
1937 mp->ps->hexline_length += 2;
1940 static void t1_getline (MP mp) {
1941 int c, l, eexec_scan;
1943 static const char eexec_str[] = "currentfile eexec";
1944 static int eexec_len = 17; /* |strlen(eexec_str)| */
1947 mp_fatal_error (mp,"unexpected end of file");
1948 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
1949 alloc_array (t1_line, 1, T1_BUF_SIZE);
1950 mp->ps->t1_cslen = 0;
1952 c = t1_getbyte (mp);
1955 while (!t1_eof ()) {
1956 if (mp->ps->t1_in_eexec == 1)
1957 c = edecrypt (mp,c);
1958 alloc_array (t1_line, 1, T1_BUF_SIZE);
1959 append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
1960 if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
1961 if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
1966 if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
1968 if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
1969 (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
1970 (t1_suffix (" RD ") || t1_suffix (" -| "))) {
1971 p = mp->ps->t1_line_ptr - 5;
1974 mp->ps->t1_cslen = l = t1_scan_num (mp, p + 1, 0);
1975 mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
1976 /* |mp->ps->cs_start| is an index now */
1977 alloc_array (t1_line, l, T1_BUF_SIZE);
1979 *mp->ps->t1_line_ptr++ = edecrypt (mp,t1_getbyte (mp));
1981 c = t1_getbyte (mp);
1983 alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
1984 append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
1985 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
1987 if (eexec_scan == eexec_len)
1988 mp->ps->t1_in_eexec = 1;
1990 /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
1991 mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
1992 alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
1995 static void t1_putline (MP mp)
1997 char *p = mp->ps->t1_line_array;
1998 if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
2000 if (mp->ps->t1_eexec_encrypt) {
2001 while (p < mp->ps->t1_line_ptr)
2002 out_eexec_char (eencrypt (mp,*p++));
2004 while (p < mp->ps->t1_line_ptr)
2009 static void t1_puts (MP mp, const char *s)
2011 if (s != mp->ps->t1_line_array)
2012 strcpy (mp->ps->t1_line_array, s);
2013 mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
2017 static void t1_printf (MP mp, const char *fmt, ...)
2020 va_start (args, fmt);
2021 vsprintf (mp->ps->t1_line_array, fmt, args);
2022 t1_puts (mp,mp->ps->t1_line_array);
2026 static void t1_init_params (MP mp, char *open_name_prefix,
2027 char *cur_file_name) {
2028 if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
2029 t1_log (open_name_prefix);
2030 t1_log (cur_file_name);
2032 mp->ps->t1_lenIV = 4;
2033 mp->ps->t1_dr = 55665;
2034 mp->ps->t1_er = 55665;
2035 mp->ps->t1_in_eexec = 0;
2036 mp->ps->t1_cs = false;
2037 mp->ps->t1_scan = true;
2038 mp->ps->t1_synthetic = false;
2039 mp->ps->t1_eexec_encrypt = false;
2040 mp->ps->t1_block_length = 0;
2043 static void t1_close_font_file (MP mp, const char *close_name_suffix) {
2044 if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
2045 t1_log (close_name_suffix);
2050 static void t1_check_block_len (MP mp, boolean decrypt) {
2053 if (mp->ps->t1_block_length == 0)
2055 c = t1_getbyte (mp);
2057 c = edecrypt (mp,c);
2058 l = mp->ps->t1_block_length;
2059 if (!(l == 0 && (c == 10 || c == 13))) {
2060 snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
2066 static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
2068 if (!mp->ps->t1_pfa)
2069 t1_check_block_len (mp, false);
2070 for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
2071 edecrypt (mp, t1_getbyte (mp));
2072 *mp->ps->t1_line_ptr++ = 0;
2074 mp->ps->t1_eexec_encrypt = true;
2075 if (!mp->ps->read_encoding_only)
2076 if (is_included (fm_cur))
2077 t1_putline (mp); /* to put the first four bytes */
2079 static void t1_stop_eexec (MP mp) {
2081 end_last_eexec_line ();
2082 if (!mp->ps->t1_pfa)
2083 t1_check_block_len (mp,true);
2085 c = edecrypt (mp, t1_getbyte (mp));
2086 if (!(c == 10 || c == 13)) {
2087 if (mp->ps->last_hexbyte == 0)
2090 mp_warn (mp,"unexpected data after eexec");
2093 mp->ps->t1_cs = false;
2094 mp->ps->t1_in_eexec = 2;
2096 static void t1_modify_fm (MP mp) {
2097 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2100 static void t1_modify_italic (MP mp) {
2101 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2106 const char *pdfname;
2116 static key_entry font_keys[FONT_KEYS_NUM] = {
2117 {"Ascent", "Ascender", 0, false},
2118 {"CapHeight", "CapHeight", 0, false},
2119 {"Descent", "Descender", 0, false},
2120 {"FontName", "FontName", 0, false},
2121 {"ItalicAngle", "ItalicAngle", 0, false},
2122 {"StemV", "StdVW", 0, false},
2123 {"XHeight", "XHeight", 0, false},
2124 {"FontBBox", "FontBBox", 0, false},
2136 @d ITALIC_ANGLE_CODE 4
2142 @d FONTBBOX4_CODE 10
2143 @d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
2146 static void t1_scan_keys (MP mp, int tex_font,fm_entry *fm_cur) {
2150 if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
2151 if (t1_prefix ("/FontMatrix")) {
2155 if (t1_prefix ("/ItalicAngle")) {
2156 t1_modify_italic (mp);
2160 if (t1_prefix ("/FontType")) {
2161 p = mp->ps->t1_line_array + strlen ("FontType") + 1;
2162 if ((i = t1_scan_num (mp,p, 0)) != 1) {
2164 snprintf(s,125,"Type%d fonts unsupported by metapost", i);
2165 mp_fatal_error(mp,s);
2169 for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
2170 if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
2172 if (key - font_keys == MAX_KEY_CODE)
2175 p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
2177 if ((k = key - font_keys) == FONTNAME_CODE) {
2180 remove_eol (p, mp->ps->t1_line_array);
2181 snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
2182 mp_fatal_error(mp,s);
2184 r = ++p; /* skip the slash */
2185 if (is_included (fm_cur)) {
2186 /* save the fontname */
2187 strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
2188 for (i=0; mp->ps->fontname_buf[i] != 10; i++);
2189 mp->ps->fontname_buf[i]=0;
2191 if(is_subsetted (fm_cur)) {
2192 if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
2193 make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
2195 make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
2197 alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
2199 strncpy (r, fm_cur->subset_tag , 6);
2201 strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
2202 mp->ps->t1_line_ptr = eol (r);
2204 /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
2206 mp->ps->t1_line_ptr = eol (r);
2211 if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
2212 && (*p == '[' || *p == '{'))
2214 if (k == FONTBBOX1_CODE) {
2215 for (i = 0; i < 4; i++) {
2216 key[i].value = t1_scan_num (mp, p, &r);
2221 key->value = t1_scan_num (mp, p, 0);
2223 static void t1_scan_param (MP mp, int tex_font,fm_entry *fm_cur)
2225 static const char *lenIV = "/lenIV";
2226 if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
2228 if (t1_prefix (lenIV)) {
2229 mp->ps->t1_lenIV = t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
2232 t1_scan_keys (mp, tex_font,fm_cur);
2234 static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
2235 if (glyph_names[b] != notdef) {
2236 mp_xfree (glyph_names[b]);
2237 glyph_names[b] = (char *) notdef;
2239 if (glyph_names[a] != notdef) {
2240 glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
2243 static void t1_builtin_enc (MP mp) {
2244 int i, a, b, c, counter = 0;
2247 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
2249 if (t1_suffix ("def")) { /* predefined encoding */
2250 sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
2251 if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
2252 for (i = 0; i < 256; i++)
2253 if (standard_glyph_names[i] == notdef)
2254 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2256 mp->ps->t1_builtin_glyph_names[i] =
2257 mp_xstrdup (mp,standard_glyph_names[i]);
2258 mp->ps->t1_encoding = ENC_STANDARD;
2261 snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
2262 mp->ps->t1_buf_array);
2263 mp_fatal_error(mp,s);
2267 mp->ps->t1_encoding = ENC_BUILTIN;
2269 * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
2270 * not a predefined encoding
2272 * We have two possible forms of Encoding vector. The first case is
2274 * /Encoding [/a /b /c...] readonly def
2276 * and the second case can look like
2278 * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
2284 for (i = 0; i < 256; i++)
2285 mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
2286 if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
2287 r = strchr (mp->ps->t1_line_array, '[') + 1;
2291 for (p = mp->ps->t1_buf_array, r++;
2292 *r != 32 && *r != 10 && *r != ']' && *r != '/';
2296 if (counter > 255) {
2298 (mp, "encoding vector contains more than 256 names");
2300 if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
2301 mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
2304 if (*r != 10 && *r != '%') {
2305 if (str_prefix (r, "] def")
2306 || str_prefix (r, "] readonly def"))
2310 remove_eol (r, mp->ps->t1_line_array);
2311 snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
2312 mp->ps->t1_line_array);
2313 mp_fatal_error(mp,s);
2317 r = mp->ps->t1_line_array;
2319 } else { /* the second case */
2320 p = strchr (mp->ps->t1_line_array, 10);
2324 p = mp->ps->t1_line_array;
2327 check for `dup <index> <glyph> put'
2329 if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
2330 *mp->ps->t1_buf_array == '/' && valid_code (i)) {
2331 if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
2332 mp->ps->t1_builtin_glyph_names[i] =
2333 mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2334 p = strstr (p, " put") + strlen (" put");
2338 check for `dup dup <to> exch <from> get put'
2340 else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
2341 && valid_code (a) && valid_code (b)) {
2342 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
2343 p = strstr (p, " get put") + strlen (" get put");
2347 check for `dup dup <from> <size> getinterval <to> exch putinterval'
2350 (p, "dup dup %i %i getinterval %i exch putinterval",
2351 &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
2352 && valid_code (c)) {
2353 for (i = 0; i < c; i++)
2354 copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
2355 p = strstr (p, " putinterval") + strlen (" putinterval");
2359 check for `def' or `readonly def'
2361 else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
2362 && strcmp (p, "def\n") == 0)
2365 skip an unrecognizable word
2368 while (*p != ' ' && *p != 10)
2376 static void t1_check_end (MP mp) {
2380 if (t1_prefix ("{restore}"))
2386 char *ff_name; /* base name of font file */
2387 char *ff_path; /* full path to font file */
2391 static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
2393 ff = check_ff_exist (mp, fm_cur);
2394 if (ff->ff_path != NULL) {
2395 mp->ps->t1_file = mp_open_file(mp,ff->ff_path, "rb", mp_filetype_font);
2397 mp_warn (mp, "cannot open Type 1 font file for reading");
2400 t1_init_params (mp,(char *)open_name_prefix,fm_cur->ff_name);
2401 mp->ps->fontfile_found = true;
2405 static void t1_scan_only (MP mp, int tex_font, fm_entry *fm_cur) {
2408 t1_scan_param (mp,tex_font, fm_cur);
2410 while (mp->ps->t1_in_eexec == 0);
2411 t1_start_eexec (mp,fm_cur);
2414 t1_scan_param (mp,tex_font, fm_cur);
2416 while (!(t1_charstrings () || t1_subrs ()));
2419 static void t1_include (MP mp, int tex_font, fm_entry *fm_cur) {
2422 t1_scan_param (mp,tex_font, fm_cur);
2425 while (mp->ps->t1_in_eexec == 0);
2426 t1_start_eexec (mp,fm_cur);
2429 t1_scan_param (mp,tex_font, fm_cur);
2432 while (!(t1_charstrings () || t1_subrs ()));
2433 mp->ps->t1_cs = true;
2438 while (!t1_end_eexec ());
2440 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
2445 while (!t1_cleartomark ());
2446 t1_check_end (mp); /* write "{restore}if" if found */
2451 @d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
2453 snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
2454 mp_fatal_error(mp,s);
2458 static const char **check_cs_token_pair (MP mp) {
2459 const char **p = (const char **) cs_token_pairs_list;
2460 for (; p[0] != NULL; ++p)
2461 if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
2466 static void cs_store (MP mp, boolean is_subr) {
2470 for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
2471 *mp->ps->t1_buf_ptr++ = *p++);
2472 *mp->ps->t1_buf_ptr = 0;
2474 subr = t1_scan_num (mp, p + 1, 0);
2476 ptr = mp->ps->subr_tab + subr;
2478 ptr = mp->ps->cs_ptr++;
2479 if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
2481 snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
2482 mp_fatal_error(mp,s);
2484 if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
2485 ptr->glyph_name = (char *) notdef;
2487 ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
2489 /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
2490 memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
2491 (unsigned) (mp->ps->t1_cslen + 4));
2492 /* copy the end of cs data to |mp->ps->t1_buf_array| */
2493 for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
2494 mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
2495 *mp->ps->t1_buf_ptr++ = 10;
2496 if (is_subr && mp->ps->cs_token_pair == NULL)
2497 mp->ps->cs_token_pair = check_cs_token_pair (mp);
2498 ptr->len = mp->ps->t1_buf_ptr - mp->ps->t1_buf_array;
2499 ptr->cslen = mp->ps->t1_cslen;
2500 ptr->data = mp_xmalloc (mp,ptr->len , sizeof (byte));
2501 memcpy (ptr->data, mp->ps->t1_buf_array, ptr->len);
2505 #define store_subr(mp) cs_store(mp,true)
2506 #define store_cs(mp) cs_store(mp,false)
2508 #define CC_STACK_SIZE 24
2510 static integer cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
2511 static cc_entry cc_tab[CS_MAX];
2512 static boolean is_cc_init = false;
2516 if (stack_ptr - cc_stack < (N)) \
2520 #define stack_error(N) { \
2522 snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
2523 (int) N, (int)(stack_ptr - cc_stack)); \
2529 #define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
2531 #define cc_push(V) *stack_ptr++ = V
2532 #define cc_clear() stack_ptr = cc_stack
2534 #define set_cc(N, B, A, C) \
2535 cc_tab[N].nargs = A; \
2536 cc_tab[N].bottom = B; \
2537 cc_tab[N].clear = C; \
2538 cc_tab[N].valid = true
2540 static void cc_init (void) {
2544 for (i = 0; i < CS_MAX; i++)
2545 cc_tab[i].valid = false;
2546 set_cc (CS_HSTEM, true, 2, true);
2547 set_cc (CS_VSTEM, true, 2, true);
2548 set_cc (CS_VMOVETO, true, 1, true);
2549 set_cc (CS_RLINETO, true, 2, true);
2550 set_cc (CS_HLINETO, true, 1, true);
2551 set_cc (CS_VLINETO, true, 1, true);
2552 set_cc (CS_RRCURVETO, true, 6, true);
2553 set_cc (CS_CLOSEPATH, false, 0, true);
2554 set_cc (CS_CALLSUBR, false, 1, false);
2555 set_cc (CS_RETURN, false, 0, false);
2557 |set_cc(CS_ESCAPE, false, 0, false);|
2559 set_cc (CS_HSBW, true, 2, true);
2560 set_cc (CS_ENDCHAR, false, 0, true);
2561 set_cc (CS_RMOVETO, true, 2, true);
2562 set_cc (CS_HMOVETO, true, 1, true);
2563 set_cc (CS_VHCURVETO, true, 4, true);
2564 set_cc (CS_HVCURVETO, true, 4, true);
2565 set_cc (CS_DOTSECTION, false, 0, true);
2566 set_cc (CS_VSTEM3, true, 6, true);
2567 set_cc (CS_HSTEM3, true, 6, true);
2568 set_cc (CS_SEAC, true, 5, true);
2569 set_cc (CS_SBW, true, 4, true);
2570 set_cc (CS_DIV, false, 2, false);
2571 set_cc (CS_CALLOTHERSUBR, false, 0, false);
2572 set_cc (CS_POP, false, 0, false);
2573 set_cc (CS_SETCURRENTPOINT, true, 2, true);
2579 @d cs_getchar(mp) cdecrypt(mp,*data++, &cr)
2581 @d mark_subr(mp,n) cs_mark(mp,0, n)
2582 @d mark_cs(mp,s) cs_mark(mp,s, 0)
2583 @d SMALL_BUF_SIZE 256
2586 static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
2587 char buf[SMALL_BUF_SIZE];
2590 va_start (args, fmt);
2591 vsprintf (buf, fmt, args);
2593 if (cs_name == NULL) {
2594 snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
2596 snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
2601 static void cs_mark (MP mp, const char *cs_name, int subr)
2607 static integer lastargOtherSubr3 = 3; /* the argument of last call to
2611 if (cs_name == NULL) {
2613 ptr = mp->ps->subr_tab + subr;
2617 if (mp->ps->cs_notdef != NULL &&
2618 (cs_name == notdef || strcmp (cs_name, notdef) == 0))
2619 ptr = mp->ps->cs_notdef;
2621 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2622 if (strcmp (ptr->glyph_name, cs_name) == 0)
2624 if (ptr == mp->ps->cs_ptr) {
2626 snprintf (s,128,"glyph `%s' undefined", cs_name);
2630 if (ptr->glyph_name == notdef)
2631 mp->ps->cs_notdef = ptr;
2634 /* only marked CharString entries and invalid entries can be skipped;
2635 valid marked subrs must be parsed to keep the stack in sync */
2636 if (!ptr->valid || (ptr->is_used && cs_name != NULL))
2638 ptr->is_used = true;
2640 cs_len = ptr->cslen;
2641 data = ptr->data + 4;
2642 for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
2644 while (cs_len > 0) {
2646 b = cs_getchar (mp);
2650 else if (b <= 250) {
2652 a = ((b - 247) << 8) + 108 + cs_getchar (mp);
2653 } else if (b <= 254) {
2655 a = -((b - 251) << 8) - 108 - cs_getchar (mp);
2658 a = (cs_getchar (mp) & 0xff) << 24;
2659 a |= (cs_getchar (mp) & 0xff) << 16;
2660 a |= (cs_getchar (mp) & 0xff) << 8;
2661 a |= (cs_getchar (mp) & 0xff) << 0;
2662 if (sizeof (integer) > 4 && (a & 0x80000000))
2667 if (b == CS_ESCAPE) {
2668 b = cs_getchar (mp) + CS_1BYTE_MAX;
2672 cs_warn (mp,cs_name, subr, "command value out of range: %i",
2678 cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
2682 if (stack_ptr - cc_stack < cc->nargs)
2683 cs_warn (mp,cs_name, subr,
2684 "less arguments on stack (%i) than required (%i)",
2685 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2686 else if (stack_ptr - cc_stack > cc->nargs)
2687 cs_warn (mp,cs_name, subr,
2688 "more arguments on stack (%i) than required (%i)",
2689 (int) (stack_ptr - cc_stack), (int) cc->nargs);
2691 switch (cc - cc_tab) {
2696 if (!mp->ps->subr_tab[a1].valid) {
2697 cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
2705 case CS_CALLOTHERSUBR:
2706 if (cc_get (-1) == 3)
2707 lastargOtherSubr3 = cc_get (-3);
2708 a1 = cc_get (-2) + 2;
2712 cc_push (lastargOtherSubr3);
2713 /* the only case when we care about the value being pushed onto
2714 stack is when POP follows CALLOTHERSUBR (changing hints by
2722 mark_cs (mp,standard_glyph_names[a1]);
2723 mark_cs (mp,standard_glyph_names[a2]);
2732 cs_error: /* an error occured during parsing */
2735 ptr->is_used = false;
2738 static void t1_subset_ascii_part (MP mp, int tex_font, fm_entry *fm_cur)
2742 while (!t1_prefix ("/Encoding")) {
2743 t1_scan_param (mp,tex_font, fm_cur);
2747 t1_builtin_enc (mp);
2748 if (is_reencoded (fm_cur))
2749 mp->ps->t1_glyph_names = external_enc ();
2751 mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
2753 |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
2754 make_subset_tag (fm_cur, t1_glyph_names, tex_font);
2755 update_subset_tag ();
2758 if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
2759 t1_puts (mp,"/Encoding StandardEncoding def\n");
2762 (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
2763 for (i = 0, j = 0; i < 256; i++) {
2764 if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
2766 t1_printf (mp,"dup %i /%s put\n", (int) t1_char (i),
2767 mp->ps->t1_glyph_names[i]);
2770 /* We didn't mark anything for the Encoding array. */
2771 /* We add "dup 0 /.notdef put" for compatibility */
2772 /* with Acrobat 5.0. */
2774 t1_puts (mp,"dup 0 /.notdef put\n");
2775 t1_puts (mp,"readonly def\n");
2779 t1_scan_param (mp,tex_font, fm_cur);
2780 if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
2783 while (mp->ps->t1_in_eexec == 0);
2786 #define t1_subr_flush(mp) t1_flush_cs(mp,true)
2787 #define t1_cs_flush(mp) t1_flush_cs(mp,false)
2789 static void cs_init (MP mp) {
2790 mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
2791 mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
2792 mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
2793 mp->ps->cs_token_pair = NULL;
2794 mp->ps->subr_tab = NULL;
2795 mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
2796 mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
2799 static void init_cs_entry ( cs_entry * cs) {
2801 cs->glyph_name = NULL;
2804 cs->is_used = false;
2808 static void t1_mark_glyphs (MP mp, int tex_font);
2810 static void t1_read_subrs (MP mp, int tex_font, fm_entry *fm_cur)
2815 while (!(t1_charstrings () || t1_subrs ())) {
2816 t1_scan_param (mp,tex_font, fm_cur);
2821 mp->ps->t1_cs = true;
2822 mp->ps->t1_scan = false;
2825 mp->ps->subr_size_pos = strlen ("/Subrs") + 1;
2826 /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
2827 mp->ps->subr_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
2828 if (mp->ps->subr_size == 0) {
2829 while (!t1_charstrings ())
2833 /* |subr_tab = xtalloc (subr_size, cs_entry);| */
2834 mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,mp->ps->subr_size, sizeof (cs_entry));
2835 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2836 init_cs_entry (ptr);
2837 mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
2839 while (mp->ps->t1_cslen) {
2843 /* mark the first four entries without parsing */
2844 for (i = 0; i < mp->ps->subr_size && i < 4; i++)
2845 mp->ps->subr_tab[i].is_used = true;
2846 /* the end of the Subrs array might have more than one line so we need to
2847 concatnate them to |subr_array_end|. Unfortunately some fonts don't have
2848 the Subrs array followed by the CharStrings dict immediately (synthetic
2849 fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
2850 we will treat the font as synthetic and ignore everything until next
2853 #define POST_SUBRS_SCAN 5
2855 *mp->ps->t1_buf_array = 0;
2856 for (i = 0; i < POST_SUBRS_SCAN; i++) {
2857 if (t1_charstrings ())
2859 s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
2860 alloc_array (t1_buf, s, T1_BUF_SIZE);
2861 strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
2864 mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
2865 if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
2866 suppose synthetic font */
2867 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2869 mp_xfree (ptr->data);
2870 mp_xfree (mp->ps->subr_tab);
2871 mp_xfree (mp->ps->subr_array_start);
2872 mp_xfree (mp->ps->subr_array_end);
2874 mp->ps->t1_cs = false;
2875 mp->ps->t1_synthetic = true;
2876 while (!(t1_charstrings () || t1_subrs ()))
2883 static void t1_flush_cs (MP mp, boolean is_subr)
2886 byte *r, *return_cs = NULL;
2887 cs_entry *tab, *end_tab, *ptr;
2888 char *start_line, *line_end;
2889 int count, size_pos;
2890 unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
2892 start_line = mp->ps->subr_array_start;
2893 line_end = mp->ps->subr_array_end;
2894 size_pos = mp->ps->subr_size_pos;
2895 tab = mp->ps->subr_tab;
2896 count = mp->ps->subr_max + 1;
2897 end_tab = mp->ps->subr_tab + count;
2899 start_line = mp->ps->cs_dict_start;
2900 line_end = mp->ps->cs_dict_end;
2901 size_pos = mp->ps->cs_size_pos;
2902 tab = mp->ps->cs_tab;
2903 end_tab = mp->ps->cs_ptr;
2904 count = mp->ps->cs_count;
2906 mp->ps->t1_line_ptr = mp->ps->t1_line_array;
2907 for (p = start_line; p - start_line < size_pos;)
2908 *mp->ps->t1_line_ptr++ = *p++;
2909 while (isdigit (*p))
2911 sprintf (mp->ps->t1_line_ptr, "%u", count);
2912 strcat (mp->ps->t1_line_ptr, p);
2913 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2916 /* create |return_cs| to replace unsused subr's */
2920 return_cs = mp_xmalloc (mp, (mp->ps->t1_lenIV + 1) , sizeof(byte));
2921 if ( mp->ps->t1_lenIV > 0) {
2922 for (cs_len = 0, r = return_cs; cs_len < mp->ps->t1_lenIV; cs_len++, r++)
2923 *r = cencrypt (mp,0x00, &cr);
2924 *r = cencrypt (mp,CS_RETURN, &cr);
2926 *return_cs = CS_RETURN;
2931 for (ptr = tab; ptr < end_tab; ptr++) {
2934 sprintf (mp->ps->t1_line_array, "dup %i %u", (int) (ptr - tab),
2937 sprintf (mp->ps->t1_line_array, "/%s %u", ptr->glyph_name, ptr->cslen);
2938 p = strend (mp->ps->t1_line_array);
2939 memcpy (p, ptr->data, ptr->len);
2940 mp->ps->t1_line_ptr = p + ptr->len;
2943 /* replace unsused subr's by |return_cs| */
2945 sprintf (mp->ps->t1_line_array, "dup %i %u%s ", (int) (ptr - tab),
2946 cs_len, mp->ps->cs_token_pair[0]);
2947 p = strend (mp->ps->t1_line_array);
2948 memcpy (p, return_cs, cs_len);
2949 mp->ps->t1_line_ptr = p + cs_len;
2951 sprintf (mp->ps->t1_line_array, " %s", mp->ps->cs_token_pair[1]);
2952 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2956 mp_xfree (ptr->data);
2957 if (ptr->glyph_name != notdef)
2958 mp_xfree (ptr->glyph_name);
2960 sprintf (mp->ps->t1_line_array, "%s", line_end);
2961 mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
2964 mp_xfree (return_cs);
2966 mp_xfree (start_line);
2967 mp_xfree (line_end);
2970 static void t1_mark_glyphs (MP mp, int tex_font)
2973 char *charset = extra_charset ();
2976 if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
2977 if (mp->ps->cs_tab != NULL)
2978 for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
2980 ptr->is_used = true;
2981 if (mp->ps->subr_tab != NULL) {
2982 for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
2984 ptr->is_used = true;
2985 mp->ps->subr_max = mp->ps->subr_size - 1;
2989 mark_cs (mp,notdef);
2990 for (i = 0; i < 256; i++)
2991 if (is_used_char (i)) {
2992 if (mp->ps->t1_glyph_names[i] == notdef) {
2994 snprintf(s,128, "character %i is mapped to %s", i, notdef);
2997 mark_cs (mp,mp->ps->t1_glyph_names[i]);
2999 if (charset == NULL)
3001 g = s = charset + 1; /* skip the first '/' */
3004 while (*s != '/' && s < r)
3006 *s = 0; /* terminate g by rewriting '/' to 0 */
3011 if (mp->ps->subr_tab != NULL)
3012 for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
3013 ptr - mp->ps->subr_tab < mp->ps->subr_size;
3015 if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
3016 mp->ps->subr_max = ptr - mp->ps->subr_tab;
3019 static void t1_subset_charstrings (MP mp, int tex_font)
3022 mp->ps->cs_size_pos =
3023 strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
3024 - mp->ps->t1_line_array + 1;
3025 /* |cs_size_pos| points to the number indicating
3026 dict size after "/CharStrings" */
3027 mp->ps->cs_size = t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
3028 mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,mp->ps->cs_size, sizeof(cs_entry));
3029 for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
3030 init_cs_entry (ptr);
3031 mp->ps->cs_notdef = NULL;
3032 mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
3034 while (mp->ps->t1_cslen) {
3038 mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
3039 t1_mark_glyphs (mp,tex_font);
3040 if (mp->ps->subr_tab != NULL) {
3041 if (mp->ps->cs_token_pair == NULL)
3043 (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
3046 for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
3052 static void t1_subset_end (MP mp)
3054 if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
3055 while (!strstr (mp->ps->t1_line_array, "definefont")) {
3059 while (!t1_end_eexec ())
3060 t1_getline (mp); /* ignore the rest */
3061 t1_putline (mp); /* write "mark currentfile closefile" */
3063 while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
3068 if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
3069 while (!t1_cleartomark ()) {
3073 if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
3074 t1_check_end (mp); /* write "{restore}if" if found */
3078 static int t1_updatefm (MP mp, int f, fm_entry *fm)
3081 mp->ps->read_encoding_only = true;
3082 if (!t1_open_fontfile (mp,fm,NULL)) {
3085 t1_scan_only (mp,f, fm);
3086 s = mp_xstrdup(mp,mp->ps->fontname_buf);
3088 while (*p != ' ' && *p != 0)
3092 t1_close_font_file (mp,"");
3097 static void writet1 (MP mp, int tex_font, fm_entry *fm_cur) {
3098 int save_selector = mp->selector;
3099 mp_normalize_selector(mp);
3100 mp->ps->read_encoding_only = false;
3101 if (!is_included (fm_cur)) { /* scan parameters from font file */
3102 if (!t1_open_fontfile (mp,fm_cur,"{"))
3104 t1_scan_only (mp,tex_font, fm_cur);
3105 t1_close_font_file (mp,"}");
3108 if (!is_subsetted (fm_cur)) { /* include entire font */
3109 if (!t1_open_fontfile (mp,fm_cur,"<<"))
3111 t1_include (mp,tex_font,fm_cur);
3112 t1_close_font_file (mp,">>");
3115 /* partial downloading */
3116 if (!t1_open_fontfile (mp,fm_cur,"<"))
3118 t1_subset_ascii_part (mp,tex_font,fm_cur);
3119 t1_start_eexec (mp,fm_cur);
3122 t1_read_subrs (mp,tex_font, fm_cur);
3123 t1_subset_charstrings (mp,tex_font);
3125 t1_close_font_file (mp,">");
3126 mp->selector = save_selector;
3130 static void t1_free (MP mp);
3133 static void t1_free (MP mp) {
3134 mp_xfree (mp->ps->t1_line_array);
3135 mp_xfree (mp->ps->t1_buf_array);
3139 @* \[44d] Embedding fonts.
3141 @ The |tfm_num| is officially of type |font_number|, but that
3142 type does not exist yet at this point in the output order.
3146 char *tfm_name; /* TFM file name */
3147 char *ps_name; /* PostScript name */
3148 integer flags; /* font flags */
3149 char *ff_name; /* font file name */
3150 char *subset_tag; /* pseudoUniqueTag for subsetted font */
3151 enc_entry *encoding; /* pointer to corresponding encoding */
3152 unsigned int tfm_num; /* number of the TFM refering this entry */
3153 unsigned short type; /* font type (T1/TTF/...) */
3154 short slant; /* SlantFont */
3155 short extend; /* ExtendFont */
3156 integer ff_objnum; /* FontFile object number */
3157 integer fn_objnum; /* FontName/BaseName object number */
3158 integer fd_objnum; /* FontDescriptor object number */
3159 char *charset; /* string containing used glyphs */
3160 boolean all_glyphs; /* embed all glyphs? */
3161 unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
3162 short tfm_avail; /* flags whether a tfm is available */
3163 short pid; /* Pid for truetype fonts */
3164 short eid; /* Eid for truetype fonts */
3170 #define FONTNAME_BUF_SIZE 128
3171 boolean fontfile_found;
3172 boolean is_otf_font;
3173 char fontname_buf[FONTNAME_BUF_SIZE];
3181 @d set_included(fm) ((fm)->type |= F_INCLUDED)
3182 @d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
3183 @d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
3184 @d set_basefont(fm) ((fm)->type |= F_BASEFONT)
3186 @d is_included(fm) ((fm)->type & F_INCLUDED)
3187 @d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
3188 @d is_truetype(fm) ((fm)->type & F_TRUETYPE)
3189 @d is_basefont(fm) ((fm)->type & F_BASEFONT)
3190 @d is_reencoded(fm) ((fm)->encoding != NULL)
3191 @d is_fontfile(fm) (fm_fontfile(fm) != NULL)
3192 @d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
3194 @d fm_slant(fm) (fm)->slant
3195 @d fm_extend(fm) (fm)->extend
3196 @d fm_fontfile(fm) (fm)->ff_name
3199 static boolean mp_font_is_reencoded (MP mp, int f);
3200 static boolean mp_font_is_included (MP mp, int f);
3201 static boolean mp_font_is_subsetted (MP mp, int f);
3204 static boolean mp_font_is_reencoded (MP mp, int f) {
3206 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3208 && (fm->ps_name != NULL)
3209 && is_reencoded (fm))
3214 static boolean mp_font_is_included (MP mp, int f) {
3216 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
3218 && (fm->ps_name != NULL && fm->ff_name != NULL)
3219 && is_included (fm))
3224 static boolean mp_font_is_subsetted (MP mp, int f) {
3226 if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
3228 && (fm->ps_name != NULL && fm->ff_name != NULL)
3229 && is_included (fm) && is_subsetted (fm))
3235 @ @<Exported function headers@>=
3236 char * mp_fm_encoding_name (MP mp, int f);
3237 char * mp_fm_font_name (MP mp, int f);
3240 static char * mp_fm_font_subset_name (MP mp, int f);
3243 @c char * mp_fm_encoding_name (MP mp, int f) {
3246 if (mp_has_fm_entry (mp, f, &fm)) {
3247 if (fm != NULL && (fm->ps_name != NULL)) {
3248 if (is_reencoded (fm)) {
3250 if (e->enc_name!=NULL)
3251 return mp_xstrdup(mp,e->enc_name);
3257 print_err ("fontmap encoding problems for font ");
3258 mp_print(mp,mp->font_name[f]);
3262 char * mp_fm_font_name (MP mp, int f) {
3264 if (mp_has_fm_entry (mp, f,&fm)) {
3265 if (fm != NULL && (fm->ps_name != NULL)) {
3266 if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
3267 /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
3268 if (t1_updatefm(mp,f,fm)) {
3269 mp->font_ps_name_fixed[f] = true;
3271 print_err ("font loading problems for font ");
3272 mp_print(mp,mp->font_name[f]);
3276 return mp_xstrdup(mp,fm->ps_name);
3279 print_err ("fontmap name problems for font ");
3280 mp_print(mp,mp->font_name[f]);
3285 static char * mp_fm_font_subset_name (MP mp, int f) {
3287 if (mp_has_fm_entry (mp, f, &fm)) {
3288 if (fm != NULL && (fm->ps_name != NULL)) {
3289 if (is_subsetted(fm)) {
3290 char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
3291 snprintf(s,strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
3294 return mp_xstrdup(mp,fm->ps_name);
3298 print_err ("fontmap name problems for font ");
3299 mp_print(mp,mp->font_name[f]);
3305 static integer mp_fm_font_slant (MP mp, int f);
3306 static integer mp_fm_font_extend (MP mp, int f);
3309 @c static integer mp_fm_font_slant (MP mp, int f) {
3311 if (mp_has_fm_entry (mp, f, &fm)) {
3312 if (fm != NULL && (fm->ps_name != NULL)) {
3318 static integer mp_fm_font_extend (MP mp, int f) {
3320 if (mp_has_fm_entry (mp, f, &fm)) {
3321 if (fm != NULL && (fm->ps_name != NULL)) {
3329 static boolean mp_do_ps_font (MP mp, font_number f);
3331 @ @c static boolean mp_do_ps_font (MP mp, font_number f) {
3333 (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
3336 if (is_truetype(fm_cur) ||
3337 (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
3340 if (is_included(fm_cur)) {
3341 mp_print_nl(mp,"%%BeginResource: font ");
3342 if (is_subsetted(fm_cur)) {
3343 mp_print(mp, fm_cur->subset_tag);
3344 mp_print_char(mp,'-');
3346 mp_print(mp, fm_cur->ps_name);
3348 writet1 (mp,f,fm_cur);
3349 mp_print_nl(mp,"%%EndResource");
3355 @ Included subset fonts do not need and encoding vector, make
3356 sure we skip that case.
3359 static void mp_list_used_resources (MP mp);
3361 @ @c static void mp_list_used_resources (MP mp) {
3362 font_number f; /* fonts used in a text node or as loop counters */
3363 int ff; /* a loop counter */
3364 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3366 int prologues = (mp->internal[mp_prologues]>>16);
3367 int procset = (mp->internal[mp_procset]>>16);
3370 mp_print_nl(mp, "%%DocumentResources: procset mpost");
3372 mp_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
3375 for (f=null_font+1;f<=mp->last_fnum;f++) {
3376 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3377 for (ff=ldf;ff>=null_font;ff--) {
3378 if ( mp_has_font_size(mp,ff) )
3379 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3382 if ( mp_font_is_subsetted(mp,f) )
3384 if ( mp->ps_offset+1+strlen(mp->font_enc_name[f])>
3385 (unsigned)mp->max_print_line )
3386 mp_print_nl(mp, "%%+ encoding");
3389 mp_print_nl(mp, "%%+ encoding");
3391 mp_print_char(mp, ' ');
3392 mp_print(mp, mp->font_enc_name[f]);
3400 for (f=null_font+1;f<=mp->last_fnum;f++) {
3401 if ( mp_has_font_size(mp,f) ) {
3402 for (ff=ldf;ff>=null_font;ff--) {
3403 if ( mp_has_font_size(mp,ff) )
3404 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3407 if ( mp->ps_offset+1+strlen(mp->font_ps_name[f])>
3408 (unsigned)mp->max_print_line )
3409 mp_print_nl(mp, "%%+ font");
3412 mp_print_nl(mp, "%%+ font");
3414 mp_print_char(mp, ' ');
3415 if ( (prologues==3)&&
3416 (mp_font_is_subsetted(mp,f)) )
3417 mp_print(mp, mp_fm_font_subset_name(mp,f));
3419 mp_print(mp, mp->font_ps_name[f]);
3429 static void mp_list_supplied_resources (MP mp);
3431 @ @c static void mp_list_supplied_resources (MP mp) {
3432 font_number f; /* fonts used in a text node or as loop counters */
3433 int ff; /* a loop counter */
3434 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3436 int prologues = (mp->internal[mp_prologues]>>16);
3437 int procset = (mp->internal[mp_procset]>>16);
3439 mp_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
3441 mp_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
3444 for (f=null_font+1;f<=mp->last_fnum;f++) {
3445 if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
3446 for (ff=ldf;ff>= null_font;ff++) {
3447 if ( mp_has_font_size(mp,ff) )
3448 if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
3451 if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
3453 if ( mp->ps_offset+1+strlen(mp->font_enc_name[f])>(unsigned)mp->max_print_line )
3454 mp_print_nl(mp, "%%+ encoding");
3457 mp_print_nl(mp, "%%+ encoding");
3459 mp_print_char(mp, ' ');
3460 mp_print(mp, mp->font_enc_name[f]);
3469 for (f=null_font+1;f<=mp->last_fnum;f++) {
3470 if ( mp_has_font_size(mp,f) ) {
3471 for (ff=ldf;ff>= null_font;ff--) {
3472 if ( mp_has_font_size(mp,ff) )
3473 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3476 if ( ! mp_font_is_included(mp,f) )
3478 if ( mp->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3479 mp_print_nl(mp, "%%+ font");
3482 mp_print_nl(mp, "%%+ font");
3484 mp_print_char(mp, ' ');
3485 if ( mp_font_is_subsetted(mp,f) )
3486 mp_print(mp, mp_fm_font_subset_name(mp,f));
3488 mp_print(mp, mp->font_ps_name[f]);
3498 @ @<Declarations...@>=
3499 static void mp_list_needed_resources (MP mp);
3501 @ @c static void mp_list_needed_resources (MP mp) {
3502 font_number f; /* fonts used in a text node or as loop counters */
3503 int ff; /* a loop counter */
3504 font_number ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3506 int prologues = (mp->internal[mp_prologues]>>16);
3509 for (f=null_font+1;f<=mp->last_fnum;f++ ) {
3510 if ( mp_has_font_size(mp,f)) {
3511 for (ff=ldf;ff>=null_font;ff--) {
3512 if ( mp_has_font_size(mp,ff) )
3513 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3516 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3518 if ( mp->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3519 mp_print_nl(mp, "%%+ font");
3522 mp_print_nl(mp, "%%DocumentNeededResources: font");
3524 mp_print_char(mp, ' ');
3525 mp_print(mp, mp->font_ps_name[f]);
3531 if ( ! firstitem ) {
3535 for (f=null_font+1;f<= mp->last_fnum;f++) {
3536 if ( mp_has_font_size(mp,f) ) {
3537 for (ff=ldf;ff>=null_font;ff-- ) {
3538 if ( mp_has_font_size(mp,ff) )
3539 if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
3542 if ((prologues==3)&&(mp_font_is_included(mp,f)) )
3544 mp_print(mp, "%%IncludeResource: font ");
3545 mp_print(mp, mp->font_ps_name[f]);
3556 static void mp_write_font_definition (MP mp, font_number f);
3560 @d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
3561 ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
3563 @c static void mp_write_font_definition(MP mp, font_number f) {
3564 int prologues = (mp->internal[mp_prologues]>>16);
3565 if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
3566 (mp_fm_font_extend(mp,f)!=0)||
3567 (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
3568 (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
3569 if ( (mp_font_is_subsetted(mp,f))&&
3570 (mp_font_is_included(mp,f))&&(prologues==3))
3571 mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
3573 mp_ps_name_out(mp, mp->font_ps_name[f],true);
3574 mp_ps_print(mp, " fcp");
3576 if ( applied_reencoding(f) ) {
3577 mp_ps_print(mp, "/Encoding ");
3578 mp_ps_print(mp, mp->font_enc_name[f]);
3579 mp_ps_print(mp, " def ");
3581 if ( mp_fm_font_slant(mp,f)!=0 ) {
3582 mp_print_int(mp, mp_fm_font_slant(mp,f));
3583 mp_ps_print(mp, " SlantFont ");
3585 if ( mp_fm_font_extend(mp,f)!=0 ) {
3586 mp_print_int(mp, mp_fm_font_extend(mp,f));
3587 mp_ps_print(mp, " ExtendFont ");
3589 if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
3590 mp_ps_print(mp, " 890 ScaleFont ");
3591 mp_ps_print(mp, " 277 SlantFont ");
3593 if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
3594 mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
3595 mp_ps_print(mp, "/Metrics 2 dict dup begin ");
3596 mp_ps_print(mp, "/space[0 -278]def ");
3597 mp_ps_print(mp, "/a12[-904 -939]def ");
3598 mp_ps_print(mp, "end def ");
3600 mp_ps_print(mp, "currentdict end");
3602 mp_ps_print_defined_name(mp,f);
3603 mp_ps_print(mp, " exch definefont pop");
3609 static void mp_ps_print_defined_name (MP mp, font_number f);
3612 @c static void mp_ps_print_defined_name(MP mp, font_number A) {
3613 int prologues = (mp->internal[mp_prologues]>>16);
3614 mp_ps_print(mp, " /");
3615 if ((mp_font_is_subsetted(mp,(A)))&&
3616 (mp_font_is_included(mp,(A)))&&(prologues==3))
3617 mp_print(mp, mp_fm_font_subset_name(mp,(A)));
3619 mp_print(mp, mp->font_ps_name[(A)]);
3620 if ( mp_xstrcmp(mp->font_name[(A)],"psyrgo")==0 )
3621 mp_ps_print(mp, "-Slanted");
3622 if ( mp_xstrcmp(mp->font_name[(A)],"zpzdr-reversed")==0 )
3623 mp_ps_print(mp, "-Reverse");
3624 if ( applied_reencoding((A)) ) {
3625 mp_ps_print(mp, "-");
3626 mp_ps_print(mp, mp->font_enc_name[(A)]);
3628 if ( mp_fm_font_slant(mp,(A))!=0 ) {
3629 mp_ps_print(mp, "-Slant_"); mp_print_int(mp, mp_fm_font_slant(mp,(A))) ;
3631 if ( mp_fm_font_extend(mp,(A))!=0 ) {
3632 mp_ps_print(mp, "-Extend_"); mp_print_int(mp, mp_fm_font_extend(mp,(A)));
3636 @ @<Include encodings and fonts for edge structure~|h|@>=
3637 mp_font_encodings(mp,mp->last_fnum,prologues==2);
3638 @<Embed fonts that are available@>
3640 @ @<Embed fonts that are available@>=
3643 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3646 for (f=null_font+1;f<=mp->last_fnum;f++) {
3647 if ( cur_fsize[f]!=null ) {
3648 if (prologues==3 ) {
3649 if ( ! mp_do_ps_font(mp,f) ) {
3650 if ( mp_has_fm_entry(mp,f, NULL) ) {
3651 print_err("Font embedding failed");
3656 cur_fsize[f]=link(cur_fsize[f]);
3657 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
3661 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3663 } while (! done_fonts);
3666 @ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
3669 mp_apply_mark_string_chars(mp, h, next_size);
3672 @ We also need to keep track of which characters are used in text nodes
3673 in the edge structure that is being shipped out. This is done by procedures
3674 that use the left-over |b3| field in the |char_info| words; i.e.,
3675 |char_info(f)(c).b3| gives the status of character |c| in font |f|.
3678 enum mp_char_mark_state {mp_unused=0, mp_used};
3681 void mp_mark_string_chars (MP mp,font_number f, str_number s) ;
3684 void mp_mark_string_chars (MP mp,font_number f, str_number s) {
3685 integer b; /* |char_base[f]| */
3686 ASCII_code bc,ec; /* only characters between these bounds are marked */
3687 pool_pointer k; /* an index into string |s| */
3691 k=mp->str_start[mp->next_str[s]]; /* str_stop */
3692 while ( k>mp->str_start[s] ){
3694 if ( (mp->str_pool[k]>=bc)&&(mp->str_pool[k]<=ec) )
3695 mp->font_info[b+mp->str_pool[k]].qqqq.b3=mp_used;
3701 void mp_unmark_font (MP mp,font_number f) ;
3704 void mp_unmark_font (MP mp,font_number f) {
3705 int k; /* an index into |font_info| */
3706 for (k= mp->char_base[f]+mp->font_bc[f];
3707 k<=mp->char_base[f]+mp->font_ec[f];
3709 mp->font_info[k].qqqq.b3=mp_unused;
3714 void mp_print_improved_prologue (MP mp, pointer h) ;
3719 void mp_print_improved_prologue (MP mp, pointer h) {
3720 quarterword next_size; /* the size index for fonts being listed */
3721 pointer *cur_fsize; /* current positions in |font_sizes| */
3722 boolean done_fonts; /* have we finished listing the fonts in the header? */
3723 font_number f; /* a font number for loops */
3725 int prologues = (mp->internal[mp_prologues]>>16);
3726 int procset = (mp->internal[mp_procset]>>16);
3727 int groffmode = (mp->internal[mp_gtroffmode]>>16);
3729 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3731 mp_list_used_resources(mp);
3732 mp_list_supplied_resources(mp);
3733 mp_list_needed_resources(mp);
3734 mp_print_nl(mp, "%%EndComments");
3735 mp_print_nl(mp, "%%BeginProlog");
3737 mp_print_nl(mp, "%%BeginResource: procset mpost");
3739 mp_print_nl(mp, "%%BeginResource: procset mpost-minimal");
3740 mp_print_nl(mp, "/bd{bind def}bind def"
3741 "/fshow {exch findfont exch scalefont setfont show}bd");
3742 if ( procset>0 ) @<Print the procset@>;
3743 mp_print_nl(mp, "/fcp{findfont dup length dict begin"
3744 "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
3745 mp_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
3746 "/fmd{/FontMatrix exch def}bd");
3747 mp_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
3748 "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
3749 if ( groffmode>0 ) {
3750 mp_print_nl(mp, "/ScaleFont{dup fmc 0 get"
3751 " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
3753 mp_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
3754 " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
3755 mp_print_nl(mp, "%%EndResource");
3756 @<Include encodings and fonts for edge structure~|h|@>;
3757 mp_print_nl(mp, "%%EndProlog");
3758 mp_print_nl(mp, "%%BeginSetup");
3760 for (f=null_font+1;f<=mp->last_fnum;f++) {
3761 if ( mp_has_font_size(mp,f) ) {
3762 if ( mp_has_fm_entry(mp,f,NULL) ) {
3763 mp_write_font_definition(mp,f);
3764 mp_ps_name_out(mp, mp->font_name[f],true);
3765 mp_ps_print_defined_name(mp,f);
3766 mp_ps_print(mp, " def");
3769 snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
3771 mp_ps_name_out(mp, mp->font_name[f],true);
3772 mp_ps_name_out(mp, mp->font_name[f],true);
3773 mp_ps_print(mp, " def");
3778 mp_print_nl(mp, "%%EndSetup");
3779 mp_print_nl(mp, "%%Page: 1 1");
3781 mp_xfree(cur_fsize);
3785 static font_number mp_print_font_comments (MP mp , pointer h);
3790 static font_number mp_print_font_comments (MP mp , pointer h) {
3791 quarterword next_size; /* the size index for fonts being listed */
3792 pointer *cur_fsize; /* current positions in |font_sizes| */
3793 int ff; /* a loop counter */
3794 boolean done_fonts; /* have we finished listing the fonts in the header? */
3795 font_number f; /* a font number for loops */
3796 scaled ds; /* design size and scale factor for a text node */
3797 font_number ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
3798 int prologues = (mp->internal[mp_prologues]>>16);
3799 cur_fsize = mp_xmalloc(mp,(mp->font_max+1),sizeof(pointer));
3800 if ( prologues>0 ) {
3801 @<Give a \.{DocumentFonts} comment listing all fonts with non-null
3802 |font_sizes| and eliminate duplicates@>;
3805 @<Make |cur_fsize| a copy of the |font_sizes| array@>;
3806 do { done_fonts=true;
3807 for (f=null_font+1;f<=mp->last_fnum;f++) {
3808 if ( cur_fsize[f]!=null ) {
3809 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
3811 if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
3813 if ( ! done_fonts ) {
3814 @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
3817 } while (! done_fonts);
3819 mp_xfree(cur_fsize);
3823 @ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
3824 for (f=null_font+1;f<= mp->last_fnum;f++)
3825 cur_fsize[f]=mp->font_sizes[f]
3827 @ It's not a good idea to make any assumptions about the |font_ps_name| entries,
3828 so we carefully remove duplicates. There is no harm in using a slow, brute-force
3831 @<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
3834 for (f=null_font+1;f<= mp->last_fnum;f++) {
3835 if ( mp->font_sizes[f]!=null ) {
3836 if ( ldf==null_font )
3837 mp_print_nl(mp, "%%DocumentFonts:");
3838 for (ff=ldf;ff>=null_font;ff--) {
3839 if ( mp->font_sizes[ff]!=null )
3840 if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
3843 if ( mp->ps_offset+1+strlen(mp->font_ps_name[f])>(unsigned)mp->max_print_line )
3844 mp_print_nl(mp, "%%+");
3845 mp_print_char(mp, ' ');
3846 mp_print(mp, mp->font_ps_name[f]);
3855 static void mp_hex_digit_out (MP mp,small_number d) {
3856 if ( d<10 ) mp_print_char(mp, d+'0');
3857 else mp_print_char(mp, d+'a'-10);
3860 @ We output the marks as a hexadecimal bit string starting at |c| or
3861 |font_bc[f]|, whichever is greater. If the output has to be truncated
3862 to avoid exceeding |emergency_line_length| the return value says where to
3863 start scanning next time.
3866 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
3869 @d emergency_line_length 255
3870 /* \ps\ output lines can be this long in unusual circumstances */
3873 static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
3874 eight_bits bc,ec; /* only encode characters between these bounds */
3875 integer lim; /* the maximum number of marks to encode before truncating */
3876 int p; /* |font_info| index for the current character */
3877 int d,b; /* used to construct a hexadecimal digit */
3878 lim=4*(emergency_line_length-mp->ps_offset-4);
3882 @<Restrict the range |bc..ec| so that it contains no unused characters
3883 at either end and has length at most |lim|@>;
3884 @<Print the initial label indicating that the bitmap starts at |bc|@>;
3885 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
3886 while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
3892 @ We could save time by setting the return value before the loop that
3893 decrements |ec|, but there is no point in being so tricky.
3895 @<Restrict the range |bc..ec| so that it contains no unused characters...@>=
3896 p=mp->char_base[f]+bc;
3897 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3900 if ( ec>=bc+lim ) ec=bc+lim-1;
3901 p=mp->char_base[f]+ec;
3902 while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
3906 @ @<Print the initial label indicating that the bitmap starts at |bc|@>=
3907 mp_print_char(mp, ' ');
3908 mp_hex_digit_out(mp, bc / 16);
3909 mp_hex_digit_out(mp, bc % 16);
3910 mp_print_char(mp, ':')
3914 @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
3916 for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
3918 mp_hex_digit_out(mp, d);
3921 if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
3924 mp_hex_digit_out(mp, d)
3927 @ Here is a simple function that determines whether there are any marked
3928 characters in font~|f| with character code at least~|c|.
3931 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
3934 static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
3935 int p; /* |font_info| index for the current character */
3936 for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
3937 if ( mp->font_info[p].qqqq.b3==mp_used )
3944 @ If the file name is so long that it can't be printed without exceeding
3945 |emergency_line_length| then there will be missing items in the \.{\%*Font:}
3946 line. We might have to repeat line in order to get the character usage
3947 information to fit within |emergency_line_length|.
3949 TODO: these two defines are also defined in mp.w!
3951 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
3952 @d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
3954 @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
3956 while ( mp_check_ps_marks(mp, f,t) ) {
3957 mp_print_nl(mp, "%*Font: ");
3958 if ( mp->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
3960 mp_print(mp, mp->font_name[f]);
3961 mp_print_char(mp, ' ');
3962 ds=(mp->font_dsize[f] + 8) / 16;
3963 mp_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
3964 if ( mp->ps_offset+12>emergency_line_length ) break;
3965 mp_print_char(mp, ' ');
3966 mp_print_scaled(mp, ds);
3967 if ( mp->ps_offset+5>emergency_line_length ) break;
3968 t=mp_ps_marks_out(mp, f,t);
3970 cur_fsize[f]=link(cur_fsize[f]);
3973 @ @<Print the procset@>=
3975 mp_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
3976 mp_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
3977 mp_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
3978 "/p{closepath}bd/n{newpath}bd");
3979 mp_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
3980 "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
3981 mp_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
3982 "/Q{grestore}bd/s{scale}bd/t{concat}bd");
3983 mp_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
3987 @ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
3988 arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
3989 might not have reasonable |font_ps_name| entries, but we just charge ahead
3990 anyway. The user should not make \&{prologues} positive if this will cause
3992 @:prologues_}{\&{prologues} primitive@>
3995 void mp_print_prologue (MP mp, pointer h);
3998 void mp_print_prologue (MP mp, pointer h) {
4001 int prologues = (mp->internal[mp_prologues]>>16);
4002 int procset = (mp->internal[mp_procset]>>16);
4003 ldf = mp_print_font_comments (mp, h);
4005 if ( (mp->internal[mp_prologues]>0) && (mp->last_ps_fnum<mp->last_fnum) )
4006 mp_read_psname_table(mp);
4007 mp_print(mp, "%%BeginProlog"); mp_print_ln(mp);
4008 if ( (prologues>0)||(procset>0) ) {
4009 if ( ldf!=null_font ) {
4010 if ( prologues>0 ) {
4011 for (f=null_font+1;f<=mp->last_fnum;f++) {
4012 if ( mp_has_font_size(mp,f) ) {
4013 mp_ps_name_out(mp, mp->font_name[f],true);
4014 mp_ps_name_out(mp, mp->font_ps_name[f],true);
4015 mp_ps_print(mp, " def");
4020 mp_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
4026 mp_print_nl(mp, "%%BeginResource: procset mpost");
4027 if ( (prologues>0)&&(ldf!=null_font) )
4029 "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
4031 mp_print_nl(mp, "/bd{bind def}bind def");
4032 @<Print the procset@>;
4033 mp_print_nl(mp, "%%EndResource");
4037 mp_print(mp, "%%EndProlog");
4038 mp_print_nl(mp, "%%Page: 1 1"); mp_print_ln(mp);
4041 @ Deciding where to break the ps output line. For the moment,
4042 it is necessary to create an exported function as well.
4044 @d ps_room(A) if ( (mp->ps_offset+(int)(A))>mp->max_print_line )
4045 mp_print_ln(mp) /* optional line break */
4048 void mp_ps_room (MP mp,int s) {
4053 void mp_ps_room (MP mp,int s) ;
4055 @ \MP\ used to have one single routine to print to both `write' files
4056 and the PostScript output. Web2c redefines ``Character |k| cannot be
4057 printed'', and that resulted in some bugs where 8-bit characters were
4058 written to the PostScript file (reported by Wlodek Bzyl).
4060 Also, Hans Hagen requested spaces to be output as "\\040" instead of
4061 a plain space, since that makes it easier to parse the result file
4064 @<Character |k| is not allowed in PostScript output@>=
4067 @ We often need to print a pair of coordinates.
4070 void mp_ps_pair_out (MP mp,scaled x, scaled y) {
4072 mp_print_scaled(mp, x); mp_print_char(mp, ' ');
4073 mp_print_scaled(mp, y); mp_print_char(mp, ' ');
4077 void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
4080 void mp_ps_print (MP mp,char *s) {
4084 void mp_ps_print_cmd (MP mp, char *l, char *s) {
4085 if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_print(mp,s); }
4086 else { ps_room(strlen(l)); mp_print(mp, l); };
4090 void mp_ps_print (MP mp,char *s) ;
4091 void mp_ps_print_cmd (MP mp, char *l, char *s) ;
4094 void mp_ps_string_out (MP mp, char *s) {
4095 ASCII_code k; /* bits to be converted to octal */
4098 if ( mp->ps_offset+5>mp->max_print_line ) {
4099 mp_print_char(mp, '\\');
4102 if ( (@<Character |k| is not allowed in PostScript output@>) ) {
4103 mp_print_char(mp, '\\');
4104 mp_print_char(mp, '0'+(k / 64));
4105 mp_print_char(mp, '0'+((k / 8) % 8));
4106 mp_print_char(mp, '0'+(k % 8));
4108 if ( (k=='(')||(k==')')||(k=='\\') ) mp_print_char(mp, '\\');
4109 mp_print_char(mp, k);
4112 mp_print_char(mp, ')');
4116 void mp_ps_string_out (MP mp, char *s) ;
4118 @ This is a define because the function does not use its |mp| argument.
4120 @d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
4123 static boolean mp_do_is_ps_name (char *s) {
4124 ASCII_code k; /* the character being checked */
4126 if ( (k<=' ')||(k>'~') ) return false;
4127 if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
4128 (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
4134 void mp_ps_name_out (MP mp, char *s, boolean lit) ;
4137 void mp_ps_name_out (MP mp, char *s, boolean lit) {
4138 ps_room(strlen(s)+2);
4139 mp_print_char(mp, ' ');
4140 if ( mp_is_ps_name(mp, s) ) {
4141 if ( lit ) mp_print_char(mp, '/');
4144 mp_ps_string_out(mp, s);
4145 if ( ! lit ) mp_ps_print(mp, "cvx ");
4146 mp_ps_print(mp, "cvn");
4151 @ These special comments described in the {\sl PostScript Language Reference
4152 Manual}, 2nd.~edition are understood by some \ps-reading programs.
4153 We can't normally output ``conforming'' \ps\ because
4154 the structuring conventions don't allow us to say ``Please make sure the
4155 following characters are downloaded and define the \.{fshow} macro to access
4158 The exact bounding box is written out if |mp_prologues<0|, although this
4159 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
4160 accurately. (Overfull boxes are avoided if an illustration is made to
4161 match a given \.{\char`\\hsize}.)
4164 void mp_print_initial_comment(MP mp,
4165 scaled minx, scaled miny, scaled maxx, scaled maxy);
4168 void mp_print_initial_comment(MP mp,
4169 scaled minx, scaled miny, scaled maxx, scaled maxy) {
4171 int prologues = (mp->internal[mp_prologues]>>16);
4172 mp_print(mp, "%!PS");
4174 mp_print(mp, "-Adobe-3.0 EPSF-3.0");
4175 mp_print_nl(mp, "%%BoundingBox: ");
4177 mp_print(mp, "0 0 0 0");
4178 } else if ( prologues<0 ) {
4179 mp_ps_pair_out(mp, minx,miny);
4180 mp_ps_pair_out(mp, maxx,maxy);
4182 mp_ps_pair_out(mp, mp_floor_scaled(mp, minx),mp_floor_scaled(mp, miny));
4183 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -maxx),-mp_floor_scaled(mp, -maxy));
4185 mp_print_nl(mp, "%%HiResBoundingBox: ");
4187 mp_print(mp, "0 0 0 0");
4189 mp_ps_pair_out(mp, minx,miny);
4190 mp_ps_pair_out(mp, maxx,maxy);
4192 mp_print_nl(mp, "%%Creator: MetaPost ");
4193 mp_print(mp, mp_metapost_version(mp));
4194 mp_print_nl(mp, "%%CreationDate: ");
4195 mp_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year])); mp_print_char(mp, '.');
4196 mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month])); mp_print_char(mp, '.');
4197 mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day])); mp_print_char(mp, ':');
4198 t=mp_round_unscaled(mp, mp->internal[mp_time]);
4199 mp_print_dd(mp, t / 60); mp_print_dd(mp, t % 60);
4200 mp_print_nl(mp, "%%Pages: 1");
4203 @ The most important output procedure is the one that gives the \ps\ version of
4206 @d gr_left_type(A) (A)->left_type_field
4207 @d gr_right_type(A) (A)->right_type_field
4208 @d gr_x_coord(A) (A)->x_coord_field
4209 @d gr_y_coord(A) (A)->y_coord_field
4210 @d gr_left_x(A) (A)->left_x_field
4211 @d gr_left_y(A) (A)->left_y_field
4212 @d gr_right_x(A) (A)->right_x_field
4213 @d gr_right_y(A) (A)->right_y_field
4214 @d gr_next_knot(A) (A)->next_field
4215 @d gr_originator(A) (A)->originator_field
4218 typedef struct mp_knot {
4219 unsigned short left_type_field;
4220 unsigned short right_type_field;
4221 scaled x_coord_field;
4222 scaled y_coord_field;
4223 scaled left_x_field;
4224 scaled left_y_field;
4225 scaled right_x_field;
4226 scaled right_y_field;
4227 struct mp_knot * next_field;
4228 quarterword originator_field;
4232 struct mp_knot * mp_gr_insert_knot (MP mp, struct mp_knot *q, scaled x, scaled y) {
4233 /* returns the inserted knot */
4234 struct mp_knot *r; /* the new knot */
4235 r= mp_xmalloc(mp, 1, sizeof (struct mp_knot));
4236 gr_next_knot(r)=gr_next_knot(q); gr_next_knot(q)=r;
4237 gr_right_x(r)=gr_right_x(q);
4238 gr_right_y(r)=gr_right_y(q);
4241 gr_right_x(q)=gr_x_coord(q);
4242 gr_right_y(q)=gr_y_coord(q);
4243 gr_left_x(r)=gr_x_coord(r);
4244 gr_left_y(r)=gr_y_coord(r);
4245 gr_left_type(r)=mp_explicit;
4246 gr_right_type(r)=mp_explicit;
4247 gr_originator(r)=mp_program_code;
4252 @ If we want to duplicate a knot node, we can say |copy_knot|:
4255 struct mp_knot *mp_gr_copy_knot (MP mp, struct mp_knot *p) {
4256 struct mp_knot *q; /* the copy */
4257 q = mp_xmalloc(mp, 1, sizeof (struct mp_knot));
4258 memcpy(q,p,sizeof (struct mp_knot));
4259 gr_next_knot(q)=NULL;
4263 @ The |copy_path| routine makes a clone of a given path.
4266 struct mp_knot *mp_gr_copy_path (MP mp, struct mp_knot *p) {
4267 struct mp_knot *q, *pp, *qq; /* for list manipulation */
4268 q=mp_gr_copy_knot(mp, p);
4272 gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
4273 qq=gr_next_knot(qq);
4274 pp=gr_next_knot(pp);
4280 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
4281 returns a pointer to the first node of the copy, if the path is a cycle,
4282 but to the final node of a non-cyclic copy. The global
4283 variable |path_tail| will point to the final node of the original path;
4284 this trick makes it easier to implement `\&{doublepath}'.
4286 All node types are assumed to be |endpoint| or |explicit| only.
4289 struct mp_knot * mp_gr_htap_ypoc (MP mp, struct mp_knot *p) {
4290 struct mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
4291 q=mp_xmalloc(mp, 1, sizeof (struct mp_knot)); /* this will correspond to |p| */
4294 gr_right_type(qq)=gr_left_type(pp);
4295 gr_left_type(qq)=gr_right_type(pp);
4296 gr_x_coord(qq)=gr_x_coord(pp);
4297 gr_y_coord(qq)=gr_y_coord(pp);
4298 gr_right_x(qq)=gr_left_x(pp);
4299 gr_right_y(qq)=gr_left_y(pp);
4300 gr_left_x(qq)=gr_right_x(pp);
4301 gr_left_y(qq)=gr_right_y(pp);
4302 gr_originator(qq)=gr_originator(pp);
4303 if ( gr_next_knot(pp)==p ) {
4305 /* mp->path_tail=pp; */ /* ? */
4308 rr=mp_xmalloc(mp, 1, sizeof (struct mp_knot));
4309 gr_next_knot(rr)=qq;
4311 pp=gr_next_knot(pp);
4315 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
4316 calling the following subroutine.
4319 void mp_do_gr_toss_knot_list (struct mp_knot *p) ;
4322 @d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
4325 void mp_do_gr_toss_knot_list (struct mp_knot * p) {
4326 struct mp_knot *q; /* the node being freed */
4327 struct mp_knot *r; /* the next node */
4340 void mp_gr_ps_path_out (MP mp, struct mp_knot *h) {
4341 struct mp_knot *p, *q; /* for scanning the path */
4342 scaled d; /* a temporary value */
4343 boolean curved; /* |true| unless the cubic is almost straight */
4345 mp_ps_print_cmd(mp, "newpath ","n ");
4346 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4347 mp_ps_print_cmd(mp, "moveto","m");
4350 if ( gr_right_type(p)==mp_endpoint ) {
4351 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4355 @<Start a new line and print the \ps\ commands for the curve from
4359 mp_ps_print_cmd(mp, " closepath"," p");
4362 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4364 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4367 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4368 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4369 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4370 mp_ps_print_cmd(mp, "curveto","c");
4372 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4373 mp_ps_print_cmd(mp, "lineto","l");
4376 @ Two types of straight lines come up often in \MP\ paths:
4377 cubics with zero initial and final velocity as created by |make_path| or
4378 |make_envelope|, and cubics with control points uniformly spaced on a line
4379 as created by |make_choices|.
4381 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4383 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4384 if ( gr_right_x(p)==gr_x_coord(p) )
4385 if ( gr_right_y(p)==gr_y_coord(p) )
4386 if ( gr_left_x(q)==gr_x_coord(q) )
4387 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4388 d=gr_left_x(q)-gr_right_x(p);
4389 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4390 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4391 { d=gr_left_y(q)-gr_right_y(p);
4392 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4393 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4396 @ The colored objects use a union to express the color parts:
4401 scaled _red_val, _green_val, _blue_val;
4404 scaled _cyan_val, _magenta_val, _yellow_val, _black_val;
4413 @d gr_start_x(A) (A)->start_x_field
4414 @d gr_stop_x(A) (A)->stop_x_field
4415 @d gr_dash_link(A) (A)->next_field
4417 @d gr_dash_list(A) (A)->list_field
4418 @d gr_dash_y(A) (A)->y_field
4421 typedef struct mp_dash_item {
4422 scaled start_x_field;
4423 scaled stop_x_field;
4424 struct mp_dash_item *next_field;
4426 typedef struct mp_dash_list {
4427 struct mp_dash_item *list_field;
4433 @d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
4436 void mp_do_gr_toss_dashes(struct mp_dash_list *dl);
4439 void mp_do_gr_toss_dashes(struct mp_dash_list *dl) {
4440 struct mp_dash_item *di, *dn;
4441 di = gr_dash_list(dl);
4443 dn = gr_dash_link(di);
4451 @ Now for outputting the actual graphic objects. First, set up some
4452 structures and access macros.
4454 @d gr_type(A) (A)->_type_field
4455 @d gr_link(A) (A)->_link_field
4456 @d gr_name_type(A) (A)->name_type_field
4457 @d gr_color_model(A) (A)->color_model_field
4458 @d gr_red_val(A) (A)->color_field.rgb._red_val
4459 @d gr_green_val(A) (A)->color_field.rgb._green_val
4460 @d gr_blue_val(A) (A)->color_field.rgb._blue_val
4461 @d gr_cyan_val(A) (A)->color_field.cmyk._cyan_val
4462 @d gr_magenta_val(A) (A)->color_field.cmyk._magenta_val
4463 @d gr_yellow_val(A) (A)->color_field.cmyk._yellow_val
4464 @d gr_black_val(A) (A)->color_field.cmyk._black_val
4465 @d gr_grey_val(A) (A)->color_field.grey._grey_val
4466 @d gr_path_p(A) (A)->path_p_field
4467 @d gr_htap_p(A) (A)->htap_p_field
4468 @d gr_pen_p(A) (A)->pen_p_field
4469 @d gr_ljoin_val(A) (A)->ljoin_field
4470 @d gr_lcap_val(A) (A)->lcap_field
4471 @d gr_dash_scale(A) (A)->dash_scale_field
4472 @d gr_miterlim_val(A) (A)->miterlim_field
4473 @d gr_pre_script(A) (A)->pre_script_field
4474 @d gr_post_script(A) (A)->post_script_field
4475 @d gr_dash_p(A) (A)->dash_p_field
4476 @d gr_text_p(A) (A)->text_p_field
4477 @d gr_font_n(A) (A)->font_n_field
4478 @d gr_width_val(A) (A)->width_field
4479 @d gr_height_val(A) (A)->height_field
4480 @d gr_depth_val(A) (A)->depth_field
4481 @d gr_tx_val(A) (A)->tx_field
4482 @d gr_ty_val(A) (A)->ty_field
4483 @d gr_txx_val(A) (A)->txx_field
4484 @d gr_txy_val(A) (A)->txy_field
4485 @d gr_tyx_val(A) (A)->tyx_field
4486 @d gr_tyy_val(A) (A)->tyy_field
4488 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4491 typedef struct mp_graphic_object {
4492 halfword _type_field;
4493 quarterword name_type_field;
4494 struct mp_graphic_object * _link_field;
4495 struct mp_knot * path_p_field;
4496 struct mp_knot * htap_p_field;
4497 struct mp_knot * pen_p_field;
4498 quarterword color_model_field;
4499 mp_color color_field;
4500 quarterword ljoin_field ;
4501 quarterword lcap_field ;
4502 scaled miterlim_field ;
4503 scaled dash_scale_field ;
4504 char *pre_script_field;
4505 char *post_script_field;
4506 struct mp_dash_list *dash_p_field;
4508 font_number font_n_field ;
4509 scaled width_field ;
4510 scaled height_field ;
4511 scaled depth_field ;
4518 } mp_graphic_object;
4519 typedef struct mp_edge_object {
4520 struct mp_graphic_object * body;
4523 @ @<Exported function headers@>=
4524 struct mp_graphic_object *mp_new_graphic_object(MP mp, int type);
4527 struct mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
4528 mp_graphic_object *p;
4529 p = mp_xmalloc(mp,1,sizeof(struct mp_graphic_object));
4530 memset(p,0,sizeof(struct mp_graphic_object));
4535 @ We need to keep track of several parameters from the \ps\ graphics state.
4537 This allows us to be sure that \ps\ has the correct values when they are
4538 needed without wasting time and space setting them unnecessarily.
4540 @d gs_red mp->ps->gs_state->red_field
4541 @d gs_green mp->ps->gs_state->green_field
4542 @d gs_blue mp->ps->gs_state->blue_field
4543 @d gs_black mp->ps->gs_state->black_field
4544 @d gs_colormodel mp->ps->gs_state->colormodel_field
4545 @d gs_ljoin mp->ps->gs_state->ljoin_field
4546 @d gs_lcap mp->ps->gs_state->lcap_field
4547 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4548 @d gs_miterlim mp->ps->gs_state->miterlim_field
4549 @d gs_dash_p mp->ps->gs_state->dash_p_field
4550 @d gs_previous mp->ps->gs_state->previous_field
4551 @d gs_dash_sc mp->ps->gs_state->dash_sc_field
4552 @d gs_width mp->ps->gs_state->width_field
4555 typedef struct _gs_state {
4557 scaled green_field ;
4559 scaled black_field ;
4560 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4561 quarterword colormodel_field ;
4562 /* the current colormodel */
4563 quarterword ljoin_field ;
4564 quarterword lcap_field ;
4565 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4566 quarterword adj_wx_field ;
4567 /* what resolution-dependent adjustment applies to the width */
4568 scaled miterlim_field ;
4569 /* the value from the last \&{setmiterlimit} command */
4570 struct mp_dash_list * dash_p_field ;
4571 /* edge structure for last \&{setdash} command */
4572 struct _gs_state * previous_field ;
4573 /* backlink to the previous |_gs_state| structure */
4574 scaled dash_sc_field ;
4575 /* scale factor used with |gs_dash_p| */
4576 scaled width_field ;
4577 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4582 struct _gs_state * gs_state;
4585 mp->ps->gs_state=NULL;
4587 @ To avoid making undue assumptions about the initial graphics state, these
4588 parameters are given special values that are guaranteed not to match anything
4589 in the edge structure being shipped out. On the other hand, the initial color
4590 should be black so that the translation of an all-black picture will have no
4591 \&{setcolor} commands. (These would be undesirable in a font application.)
4592 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4593 to recover from a situation where we have lost track of the graphics state.
4596 void mp_gs_unknown_graphics_state (MP mp,scaled c) ;
4599 @d mp_void (null+1) /* a null pointer different from |null| */
4601 @c void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4602 struct _gs_state *p; /* to shift graphic states around */
4603 if ( (c==0)||(c==-1) ) {
4604 if ( mp->ps->gs_state==NULL ) {
4605 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4608 while ( gs_previous!=NULL ) {
4610 mp_xfree(mp->ps->gs_state);
4614 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4615 gs_colormodel=mp_uninitialized_model;
4622 } else if ( c==1 ) {
4623 p= mp->ps->gs_state;
4624 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4625 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4627 } else if ( c==2 ) {
4629 mp_xfree(mp->ps->gs_state);
4635 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4636 that \ps's idea of the graphics state agrees with what is stored in the object.
4639 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) ;
4642 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) {
4643 /* get ready to output graphical object |p| */
4644 struct mp_knot *pp; /* for list manipulation */
4645 struct mp_dash_list *hh;
4646 scaled wx,wy,ww; /* dimensions of pen bounding box */
4647 boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4648 integer tx,ty; /* temporaries for computing |adj_wx| */
4649 scaled scf; /* a scale factor for the dash pattern */
4650 if ( gr_has_color(p) )
4651 @<Make sure \ps\ will use the right color for object~|p|@>;
4652 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) )
4653 if ( gr_pen_p(p)!=NULL )
4654 if ( pen_is_elliptical(gr_pen_p(p)) ) {
4655 @<Generate \ps\ code that sets the stroke width to the
4656 appropriate rounded value@>;
4657 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4658 @<Decide whether the line cap parameter matters and set it if necessary@>;
4659 @<Set the other numeric parameters as needed for object~|p|@>;
4661 if ( mp->ps_offset>0 ) mp_print_ln(mp);
4664 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4665 if ( gr_type(p)==mp_stroked_code )
4666 if ( (gr_left_type(gr_path_p(p))==mp_endpoint)||(gr_dash_p(p)!=NULL) )
4667 if ( gs_lcap!=gr_lcap_val(p) ) {
4669 mp_print_char(mp, ' ');
4670 mp_print_char(mp, '0'+gr_lcap_val(p));
4671 mp_ps_print_cmd(mp, " setlinecap"," lc");
4672 gs_lcap=gr_lcap_val(p);
4675 @ @<Set the other numeric parameters as needed for object~|p|@>=
4676 if ( gs_ljoin!=gr_ljoin_val(p) ) {
4678 mp_print_char(mp, ' ');
4679 mp_print_char(mp, '0'+gr_ljoin_val(p));
4680 mp_ps_print_cmd(mp, " setlinejoin"," lj");
4681 gs_ljoin=gr_ljoin_val(p);
4683 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4685 mp_print_char(mp, ' ');
4686 mp_print_scaled(mp, gr_miterlim_val(p));
4687 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4688 gs_miterlim=gr_miterlim_val(p);
4691 @ @<Make sure \ps\ will use the right color for object~|p|@>=
4693 if ( (gr_color_model(p)==mp_rgb_model)||
4694 ((gr_color_model(p)==mp_uninitialized_model)&&
4695 ((mp->internal[mp_default_color_model]>>16)==mp_rgb_model)) ) {
4696 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=gr_red_val(p))||
4697 (gs_green!=gr_green_val(p))||(gs_blue!=gr_blue_val(p)) ) {
4698 gs_red=gr_red_val(p);
4699 gs_green=gr_green_val(p);
4700 gs_blue=gr_blue_val(p);
4702 gs_colormodel=mp_rgb_model;
4704 mp_print_char(mp, ' ');
4705 mp_print_scaled(mp, gs_red); mp_print_char(mp, ' ');
4706 mp_print_scaled(mp, gs_green); mp_print_char(mp, ' ');
4707 mp_print_scaled(mp, gs_blue);
4708 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4711 } else if ( (gr_color_model(p)==mp_cmyk_model)||
4712 ((gr_color_model(p)==mp_uninitialized_model)&&
4713 ((mp->internal[mp_default_color_model]>>16)==mp_cmyk_model)) ) {
4714 if ( (gs_red!=gr_cyan_val(p))||(gs_green!=gr_magenta_val(p))||
4715 (gs_blue!=gr_yellow_val(p))||(gs_black!=gr_black_val(p))||
4716 (gs_colormodel!=mp_cmyk_model) ) {
4717 if ( gr_color_model(p)==mp_uninitialized_model ) {
4723 gs_red=gr_cyan_val(p);
4724 gs_green=gr_magenta_val(p);
4725 gs_blue=gr_yellow_val(p);
4726 gs_black=gr_black_val(p);
4728 gs_colormodel=mp_cmyk_model;
4730 mp_print_char(mp, ' ');
4731 mp_print_scaled(mp, gs_red); mp_print_char(mp, ' ');
4732 mp_print_scaled(mp, gs_green); mp_print_char(mp, ' ');
4733 mp_print_scaled(mp, gs_blue); mp_print_char(mp, ' ');
4734 mp_print_scaled(mp, gs_black);
4735 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4738 } else if ( (gr_color_model(p)==mp_grey_model)||
4739 ((gr_color_model(p)==mp_uninitialized_model)&&
4740 ((mp->internal[mp_default_color_model]>>16)==mp_grey_model)) ) {
4741 if ( (gs_red!=gr_grey_val(p))||(gs_colormodel!=mp_grey_model) ) {
4742 gs_red = gr_grey_val(p);
4746 gs_colormodel=mp_grey_model;
4748 mp_print_char(mp, ' ');
4749 mp_print_scaled(mp, gs_red);
4750 mp_ps_print_cmd(mp, " setgray"," G");
4754 if ( gr_color_model(p)==mp_no_model )
4755 gs_colormodel=mp_no_model;
4758 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4759 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4760 @:setwidth}{\&{setwidth}command@>
4761 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4762 generate \ps\ code that computes the rounded value. For non-circular pens, the
4763 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4764 have the computed width.
4766 Rounding the width to whole pixels is not likely to improve the appearance of
4767 diagonal or curved strokes, but we do it anyway for consistency. The
4768 \&{truncate} command generated here tends to make all the strokes a little
4769 @:truncate}{\&{truncate} command@>
4770 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4771 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4772 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4775 @<Generate \ps\ code that sets the stroke width...@>=
4776 @<Set |wx| and |wy| to the width and height of the bounding box for
4778 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4779 important and set |adj_wx| and |ww| accordingly@>;
4780 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4783 mp_print_char(mp, ' '); mp_print_scaled(mp, ww);
4785 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4787 if ( mp->internal[mp_procset]>0 ) {
4789 mp_print_char(mp, ' ');
4790 mp_print_scaled(mp, ww);
4791 mp_ps_print(mp, " vlw");
4794 mp_print(mp, " 0 "); mp_print_scaled(mp, ww);
4795 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4802 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4804 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4805 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4806 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4808 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4809 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4812 @ The path is considered ``essentially horizontal'' if its range of
4813 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4814 vertical'' paths are detected similarly. This code ensures that no component
4815 of the pen transformation is more that |aspect_bound*(ww+1)|.
4817 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4818 more than this factor */
4823 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4825 if ( mp_gr_coord_rangeOK(gr_path_p(p), do_y_loc, wy) ) tx=aspect_bound;
4826 else if ( mp_gr_coord_rangeOK(gr_path_p(p), do_x_loc, wx) ) ty=aspect_bound;
4827 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
4828 else { ww=wx; adj_wx=true; }
4830 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4831 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4832 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4833 bounding-box computation.
4836 boolean mp_gr_coord_rangeOK (struct mp_knot *h,
4837 small_number zoff, scaled dz);
4840 boolean mp_gr_coord_rangeOK (struct mp_knot *h,
4841 small_number zoff, scaled dz) {
4842 struct mp_knot *p; /* for scanning the path form |h| */
4843 scaled zlo,zhi; /* coordinate range so far */
4844 scaled z; /* coordinate currently being tested */
4845 if (zoff==do_x_loc) {
4849 while ( gr_right_type(p)!=mp_endpoint ) {
4851 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4852 p=gr_next_knot(p); z=gr_left_x(p);
4853 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4855 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4862 while ( gr_right_type(p)!=mp_endpoint ) {
4864 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4865 p=gr_next_knot(p); z=gr_left_y(p);
4866 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4868 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4875 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4877 else if ( z>zhi ) zhi=z;
4878 if ( zhi-zlo>dz ) return false
4880 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4881 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4882 @:stroke}{\&{stroke} command@>
4883 @:fill}{\&{fill} command@>
4884 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4887 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4888 if ( gr_type(p)==mp_fill_code ) {
4892 scf=mp_gr_get_pen_scale(mp, gr_pen_p(p));
4894 if ( gs_width==0 ) scf=gr_dash_scale(p); else hh=NULL;
4896 scf=mp_make_scaled(mp, gs_width,scf);
4897 scf=mp_take_scaled(mp, scf,gr_dash_scale(p));
4901 if ( gs_dash_p!=NULL ) {
4902 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4905 } else if ( (gs_dash_sc!=scf) || ! mp_gr_same_dashes(gs_dash_p,hh) ) {
4906 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
4910 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) ;
4914 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) {
4915 return mp_sqrt_det(mp,
4916 gr_left_x(p)-gr_x_coord(p), gr_right_x(p)-gr_x_coord(p),
4917 gr_left_y(p)-gr_y_coord(p), gr_right_y(p)-gr_y_coord(p));
4921 @ Translating a dash list into \ps\ is very similar to printing it symbolically
4922 in |print_edges|. A dash pattern with |dash_y(hh)=0| has length zero and is
4923 ignored. The same fate applies in the bizarre case of a dash pattern that
4924 cannot be printed without overflow.
4926 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
4929 if ( (gr_dash_p(p)==NULL) ||
4930 (gr_dash_y(hh)==0) ||
4931 ((abs(gr_dash_y(hh)) / unity) >= (el_gordo / scf))) {
4932 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4934 struct mp_dash_item *dpp=gr_dash_list(hh);
4935 struct mp_dash_item *pp= dpp;
4938 while ( dpp!=NULL ) {
4940 dx = mp_take_scaled(mp, gr_stop_x(dpp)-gr_start_x(dpp),scf);
4942 if (gr_dash_link(dpp)!=NULL) {
4943 dy = mp_take_scaled(mp, gr_start_x(gr_dash_link(dpp))-gr_stop_x(dpp),scf);
4945 dy = mp_take_scaled(mp, (gr_start_x(pp)+gr_dash_y(hh))-gr_stop_x(dpp),scf);
4947 mp_ps_pair_out(mp, dx, dy);
4948 dpp=gr_dash_link(dpp);
4952 mp_print_scaled(mp, mp_take_scaled(mp, mp_gr_dash_offset(mp, hh),scf));
4953 mp_ps_print_cmd(mp, " setdash"," sd");
4958 boolean mp_gr_same_dashes (struct mp_dash_list *h, struct mp_dash_list *hh) ;
4961 boolean mp_gr_same_dashes (struct mp_dash_list * h, struct mp_dash_list *hh) {
4962 /* do |h| and |hh| represent the same dash pattern? */
4963 struct mp_dash_item * p, *pp; /* dash nodes being compared */
4964 if ( h==hh ) return true;
4965 else if ( (h==NULL)||(hh==NULL) ) return false;
4966 else if ( gr_dash_y(h)!=gr_dash_y(hh) ) return false;
4967 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
4968 return false; /* can't happen */
4971 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
4972 { p=gr_dash_list(h);
4973 pp=gr_dash_list(hh);
4974 while ( (p!=NULL)&&(pp!=NULL) ) {
4975 if ( (gr_start_x(p)!=gr_start_x(pp))||
4976 (gr_stop_x(p)!=gr_stop_x(pp)) ) {
4980 pp=gr_dash_link(pp);
4987 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) ;
4990 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) {
4991 scaled x; /* the answer */
4992 if ( h==NULL || (gr_dash_list(h)==NULL) || (gr_dash_y(h)<0) )
4993 mp_confusion(mp, "dash0");
4994 @:this can't happen dash0}{\quad dash0@>
4995 if ( gr_dash_y(h)==0 ) {
4998 x=-(gr_start_x(gr_dash_list(h)) % gr_dash_y(h));
4999 if ( x<0 ) x=x+gr_dash_y(h);
5004 @ When stroking a path with an elliptical pen, it is necessary to transform
5005 the coordinate system so that a unit circular pen will have the desired shape.
5006 To keep this transformation local, we enclose it in a
5007 $$\&{gsave}\ldots\&{grestore}$$
5008 block. Any translation component must be applied to the path being stroked
5009 while the rest of the transformation must apply only to the pen.
5010 If |fill_also=true|, the path is to be filled as well as stroked so we must
5011 insert commands to do this after giving the path.
5014 void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) ;
5017 @c void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) {
5018 /* generate an elliptical pen stroke from object |h| */
5019 scaled txx,txy,tyx,tyy; /* transformation parameters */
5020 struct mp_knot *p; /* the pen to stroke with */
5021 scaled d1,det; /* for tweaking transformation parameters */
5022 integer s; /* also for tweaking transformation paramters */
5023 boolean transformed; /* keeps track of whether gsave/grestore are needed */
5025 @<Use |pen_p(h)| to set the transformation parameters and give the initial
5027 @<Tweak the transformation parameters so the transformation is nonsingular@>;
5028 mp_gr_ps_path_out(mp, gr_path_p(h));
5029 if ( mp->internal[mp_procset]==0 ) {
5030 if ( fill_also ) mp_print_nl(mp, "gsave fill grestore");
5031 @<Issue \ps\ commands to transform the coordinate system@>;
5032 mp_ps_print(mp, " stroke");
5033 if ( transformed ) mp_ps_print(mp, " grestore");
5035 if ( fill_also ) mp_print_nl(mp, "B"); else mp_print_ln(mp);
5036 if ( (txy!=0)||(tyx!=0) ) {
5038 mp_ps_pair_out(mp, txx,tyx);
5039 mp_ps_pair_out(mp, txy,tyy);
5040 mp_ps_print(mp, "0 0] t");
5041 } else if ((txx!=unity)||(tyy!=unity) ) {
5042 mp_ps_pair_out(mp,txx,tyy);
5045 mp_ps_print(mp, " S");
5046 if ( transformed ) mp_ps_print(mp, " Q");
5051 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5057 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5058 mp_print_nl(mp, "");
5059 mp_ps_print_cmd(mp, "gsave ","q ");
5060 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5061 mp_ps_print(mp, "translate ");
5068 mp_print_nl(mp, "");
5070 @<Adjust the transformation to account for |gs_width| and output the
5071 initial \&{gsave} if |transformed| should be |true|@>
5073 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5074 if ( gs_width!=unity ) {
5075 if ( gs_width==0 ) {
5076 txx=unity; tyy=unity;
5078 txx=mp_make_scaled(mp, txx,gs_width);
5079 txy=mp_make_scaled(mp, txy,gs_width);
5080 tyx=mp_make_scaled(mp, tyx,gs_width);
5081 tyy=mp_make_scaled(mp, tyy,gs_width);
5084 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5085 if ( (! transformed) ){
5086 mp_ps_print_cmd(mp, "gsave ","q ");
5091 @ @<Issue \ps\ commands to transform the coordinate system@>=
5092 if ( (txy!=0)||(tyx!=0) ){
5094 mp_print_char(mp, '[');
5095 mp_ps_pair_out(mp, txx,tyx);
5096 mp_ps_pair_out(mp, txy,tyy);
5097 mp_ps_print(mp, "0 0] concat");
5098 } else if ( (txx!=unity)||(tyy!=unity) ){
5100 mp_ps_pair_out(mp, txx,tyy);
5101 mp_print(mp, "scale");
5104 @ The \ps\ interpreter will probably abort if it encounters a singular
5105 transformation matrix. The determinant must be large enough to ensure that
5106 the printed representation will be nonsingular. Since the printed
5107 representation is always within $2^{-17}$ of the internal |scaled| value, the
5108 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5109 the magnitudes of |txx/65536|, |txy/65536|, etc.
5111 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5112 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5114 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5115 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5116 d1=4*aspect_bound+1;
5117 if ( abs(det)<d1 ) {
5118 if ( det>=0 ) { d1=d1-det; s=1; }
5119 else { d1=-d1-det; s=-1; };
5121 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5122 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5123 else txx=txx+(d1+s*abs(tyy)) / tyy;
5125 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5126 else txy=txy+(d1+s*abs(tyx)) / tyx;
5130 @ Here is a simple routine that just fills a cycle.
5133 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p);
5136 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p) { /* fill cyclic path~|p| */
5137 mp_gr_ps_path_out(mp, p);
5138 mp_ps_print_cmd(mp, " fill"," F");
5142 @ A text node may specify an arbitrary transformation but the usual case
5143 involves only shifting, scaling, and occasionally rotation. The purpose
5144 of |choose_scale| is to select a scale factor so that the remaining
5145 transformation is as ``nice'' as possible. The definition of ``nice''
5146 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5147 nice because they work out well for bitmap fonts. The code here selects
5148 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5149 non-shifting part of the transformation matrix. It is careful to avoid
5150 additions that might cause undetected overflow.
5153 scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) ;
5155 @ @c scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) {
5156 /* |p| should point to a text node */
5157 scaled a,b,c,d,ad,bc; /* temporary values */
5162 if ( (a<0) ) negate(a);
5163 if ( (b<0) ) negate(b);
5164 if ( (c<0) ) negate(c);
5165 if ( (d<0) ) negate(d);
5168 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5173 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5175 @<Exported function headers@>=
5176 void mp_gr_ship_out (MP mp, struct mp_graphic_object *h) ;
5179 void mp_gr_ship_out (MP mp, struct mp_graphic_object *h) {
5180 struct mp_graphic_object *p;
5181 scaled ds,scf; /* design size and scale factor for a text node */
5182 boolean transformed; /* is the coordinate system being transformed? */
5184 mp_gs_unknown_graphics_state(mp, 0);
5186 if ( gr_has_color(p) ) {
5187 if ( (gr_pre_script(p))!=NULL ) {
5188 mp_print_nl (mp, gr_pre_script(p));
5192 mp_gr_fix_graphics_state(mp, p);
5193 switch (gr_type(p)) {
5195 if ( gr_pen_p(p)==NULL ) mp_gr_ps_fill_out(mp, gr_path_p(p));
5196 else if ( pen_is_elliptical(gr_pen_p(p)) ) mp_gr_stroke_ellipse(mp, p,true);
5198 mp_gr_ps_fill_out(mp, gr_path_p(p));
5199 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5201 if ( gr_post_script(p)!=NULL ) {
5202 mp_print_nl (mp, gr_post_script(p)); mp_print_ln(mp);
5205 case mp_stroked_code:
5206 if ( pen_is_elliptical(gr_pen_p(p)) ) mp_gr_stroke_ellipse(mp, p,false);
5208 mp_gr_ps_fill_out(mp, gr_path_p(p));
5210 if ( gr_post_script(p)!=NULL ) {
5211 mp_print_nl (mp, gr_post_script(p)); mp_print_ln(mp);
5215 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5216 if ( mp->internal[mp_prologues]>0 )
5217 scf=mp_gr_choose_scale(mp, p);
5219 scf=mp_indexed_size(mp, gr_font_n(p), gr_name_type(p));
5220 @<Shift or transform as necessary before outputting text node~|p| at scale
5221 factor~|scf|; set |transformed:=true| if the original transformation must
5223 mp_ps_string_out(mp, gr_text_p(p));
5224 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5225 @<Print the size information and \ps\ commands for text node~|p|@>;
5228 if ( gr_post_script(p)!=NULL ) {
5229 mp_print_nl (mp, gr_post_script(p)); mp_print_ln(mp);
5232 case mp_start_clip_code:
5233 mp_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5234 mp_gr_ps_path_out(mp, gr_path_p(p));
5235 mp_ps_print_cmd(mp, " clip"," W");
5237 if ( mp->internal[mp_restore_clip_color]>0 )
5238 mp_gs_unknown_graphics_state(mp, 1);
5240 case mp_stop_clip_code:
5241 mp_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5243 if ( mp->internal[mp_restore_clip_color]>0 )
5244 mp_gs_unknown_graphics_state(mp, 2);
5246 mp_gs_unknown_graphics_state(mp, -1);
5248 case mp_start_bounds_code:
5249 case mp_stop_bounds_code:
5251 } /* all cases are enumerated */
5254 mp_ps_print_cmd(mp, "showpage","P"); mp_print_ln(mp);
5255 mp_print(mp, "%%EOF"); mp_print_ln(mp);
5256 mp_gr_toss_objects(mp, h);
5259 @ The envelope of a cyclic path~|q| could be computed by calling
5260 |make_envelope| once for |q| and once for its reversal. We don't do this
5261 because it would fail color regions that are covered by the pen regardless
5262 of where it is placed on~|q|.
5264 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5265 if ( gr_left_type(q)!=mp_endpoint ) {
5266 gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5267 gr_right_type(q)=mp_endpoint;
5272 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5274 mp_print_char(mp, ' ');
5275 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5276 mp_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5277 mp_print(mp, " fshow");
5279 mp_ps_print_cmd(mp, " grestore"," Q")
5283 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5284 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5285 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5286 if ( transformed ) {
5287 mp_ps_print_cmd(mp, "gsave [", "q [");
5288 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5289 mp_make_scaled(mp, gr_tyx_val(p),scf));
5290 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5291 mp_make_scaled(mp, gr_tyy_val(p),scf));
5292 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5293 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5295 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5296 mp_ps_print_cmd(mp, "moveto","m");
5302 @d mp_gr_toss_objects(A,B) mp_do_gr_toss_objects(B)
5305 void mp_do_gr_toss_objects (struct mp_graphic_object *p) ;
5308 void mp_do_gr_toss_objects (struct mp_graphic_object *p) {
5309 struct mp_graphic_object *q;
5311 switch (gr_type(p)) {
5313 mp_xfree(gr_pre_script(p));
5314 mp_xfree(gr_post_script(p));
5315 mp_gr_toss_knot_list(mp,gr_pen_p(p));
5316 mp_gr_toss_knot_list(mp,gr_path_p(p));
5317 mp_gr_toss_knot_list(mp,gr_htap_p(p));
5319 case mp_stroked_code:
5320 mp_xfree(gr_pre_script(p));
5321 mp_xfree(gr_post_script(p));
5322 mp_gr_toss_knot_list(mp,gr_pen_p(p));
5323 mp_gr_toss_knot_list(mp,gr_path_p(p));
5324 if (gr_dash_p(p)!=NULL)
5325 mp_gr_toss_dashes (mp,gr_dash_p(p));
5328 mp_xfree(gr_pre_script(p));
5329 mp_xfree(gr_post_script(p));
5330 mp_xfree(gr_text_p(p));
5332 case mp_start_clip_code:
5333 case mp_stop_clip_code:
5334 mp_gr_toss_knot_list(mp,gr_path_p(p));
5336 case mp_start_bounds_code:
5337 case mp_stop_bounds_code:
5339 } /* all cases are enumerated */