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, pointer h,
4165 scaled minx, scaled miny, scaled maxx, scaled maxy);
4168 void mp_print_initial_comment(MP mp, pointer h,
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 */
4338 void mp_gr_ps_path_out (MP mp, struct mp_knot *h) {
4339 struct mp_knot *p, *q; /* for scanning the path */
4340 scaled d; /* a temporary value */
4341 boolean curved; /* |true| unless the cubic is almost straight */
4343 mp_ps_print_cmd(mp, "newpath ","n ");
4344 mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
4345 mp_ps_print_cmd(mp, "moveto","m");
4348 if ( gr_right_type(p)==mp_endpoint ) {
4349 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
4353 @<Start a new line and print the \ps\ commands for the curve from
4357 mp_ps_print_cmd(mp, " closepath"," p");
4360 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
4362 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
4365 mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
4366 mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
4367 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4368 mp_ps_print_cmd(mp, "curveto","c");
4370 mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
4371 mp_ps_print_cmd(mp, "lineto","l");
4374 @ Two types of straight lines come up often in \MP\ paths:
4375 cubics with zero initial and final velocity as created by |make_path| or
4376 |make_envelope|, and cubics with control points uniformly spaced on a line
4377 as created by |make_choices|.
4379 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
4381 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
4382 if ( gr_right_x(p)==gr_x_coord(p) )
4383 if ( gr_right_y(p)==gr_y_coord(p) )
4384 if ( gr_left_x(q)==gr_x_coord(q) )
4385 if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
4386 d=gr_left_x(q)-gr_right_x(p);
4387 if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
4388 if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
4389 { d=gr_left_y(q)-gr_right_y(p);
4390 if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
4391 if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
4394 @ The colored objects use a union to express the color parts:
4399 scaled red_val, green_val, blue_val;
4402 scaled cyan_val, magenta_val, yellow_val, black_val;
4411 @d gr_start_x(A) (A)->start_x_field
4412 @d gr_stop_x(A) (A)->stop_x_field
4413 @d gr_dash_link(A) (A)->next_field
4415 @d gr_dash_list(A) (A)->list_field
4416 @d gr_dash_y(A) (A)->y_field
4419 typedef struct mp_dash_item {
4420 scaled start_x_field;
4421 scaled stop_x_field;
4422 struct mp_dash_item *next_field;
4424 typedef struct mp_dash_list {
4425 struct mp_dash_item *list_field;
4430 @ Now for outputting the actual graphic objects. First, set up some
4431 structures and access macros.
4433 @d gr_type(A) (A)->_type_field
4434 @d gr_link(A) (A)->_link_field
4435 @d gr_name_type(A) (A)->name_type_field
4436 @d gr_color_model(A) (A)->color_model_field
4437 @d gr_red_val(A) (A)->color_field.rgb.red_val
4438 @d gr_green_val(A) (A)->color_field.rgb.green_val
4439 @d gr_blue_val(A) (A)->color_field.rgb.blue_val
4440 @d gr_cyan_val(A) (A)->color_field.cmyk.cyan_val
4441 @d gr_magenta_val(A) (A)->color_field.cmyk.magenta_val
4442 @d gr_yellow_val(A) (A)->color_field.cmyk.yellow_val
4443 @d gr_black_val(A) (A)->color_field.cmyk.black_val
4444 @d gr_grey_val(A) (A)->color_field.grey.grey_val
4445 @d gr_path_p(A) (A)->path_p_field
4446 @d gr_htap_p(A) (A)->htap_p_field
4447 @d gr_pen_p(A) (A)->pen_p_field
4448 @d gr_ljoin_val(A) (A)->ljoin_field
4449 @d gr_lcap_val(A) (A)->lcap_field
4450 @d gr_dash_scale(A) (A)->dash_scale_field
4451 @d gr_miterlim_val(A) (A)->miterlim_field
4452 @d gr_pre_script(A) (A)->pre_script_field
4453 @d gr_post_script(A) (A)->post_script_field
4454 @d gr_dash_p(A) (A)->dash_p_field
4455 @d gr_text_p(A) (A)->text_p_field
4456 @d gr_font_n(A) (A)->font_n_field
4457 @d gr_width_val(A) (A)->width_field
4458 @d gr_height_val(A) (A)->height_field
4459 @d gr_depth_val(A) (A)->depth_field
4460 @d gr_tx_val(A) (A)->tx_field
4461 @d gr_ty_val(A) (A)->ty_field
4462 @d gr_txx_val(A) (A)->txx_field
4463 @d gr_txy_val(A) (A)->txy_field
4464 @d gr_tyx_val(A) (A)->tyx_field
4465 @d gr_tyy_val(A) (A)->tyy_field
4467 @d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
4470 typedef struct mp_graphic_object {
4471 halfword _type_field;
4472 quarterword name_type_field;
4473 struct mp_graphic_object * _link_field;
4474 struct mp_knot * path_p_field;
4475 struct mp_knot * htap_p_field;
4476 struct mp_knot * pen_p_field;
4477 quarterword color_model_field;
4478 mp_color color_field;
4479 quarterword ljoin_field ;
4480 quarterword lcap_field ;
4481 scaled miterlim_field ;
4482 scaled dash_scale_field ;
4483 char *pre_script_field;
4484 char *post_script_field;
4485 struct mp_dash_list *dash_p_field;
4487 font_number font_n_field ;
4488 scaled width_field ;
4489 scaled height_field ;
4490 scaled depth_field ;
4497 } mp_graphic_object;
4500 @ We need to keep track of several parameters from the \ps\ graphics state.
4502 This allows us to be sure that \ps\ has the correct values when they are
4503 needed without wasting time and space setting them unnecessarily.
4505 @d gs_red mp->ps->gs_state->red_field
4506 @d gs_green mp->ps->gs_state->green_field
4507 @d gs_blue mp->ps->gs_state->blue_field
4508 @d gs_black mp->ps->gs_state->black_field
4509 @d gs_colormodel mp->ps->gs_state->colormodel_field
4510 @d gs_ljoin mp->ps->gs_state->ljoin_field
4511 @d gs_lcap mp->ps->gs_state->lcap_field
4512 @d gs_adj_wx mp->ps->gs_state->adj_wx_field
4513 @d gs_miterlim mp->ps->gs_state->miterlim_field
4514 @d gs_dash_p mp->ps->gs_state->dash_p_field
4515 @d gs_previous mp->ps->gs_state->previous_field
4516 @d gs_dash_sc mp->ps->gs_state->dash_sc_field
4517 @d gs_width mp->ps->gs_state->width_field
4520 typedef struct _gs_state {
4522 scaled green_field ;
4524 scaled black_field ;
4525 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
4526 quarterword colormodel_field ;
4527 /* the current colormodel */
4528 quarterword ljoin_field ;
4529 quarterword lcap_field ;
4530 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
4531 quarterword adj_wx_field ;
4532 /* what resolution-dependent adjustment applies to the width */
4533 scaled miterlim_field ;
4534 /* the value from the last \&{setmiterlimit} command */
4535 struct mp_dash_list * dash_p_field ;
4536 /* edge structure for last \&{setdash} command */
4537 struct _gs_state * previous_field ;
4538 /* backlink to the previous |_gs_state| structure */
4539 scaled dash_sc_field ;
4540 /* scale factor used with |gs_dash_p| */
4541 scaled width_field ;
4542 /* width setting or $-1$ if no \&{setlinewidth} command so far */
4547 struct _gs_state * gs_state;
4550 mp->ps->gs_state=NULL;
4552 @ To avoid making undue assumptions about the initial graphics state, these
4553 parameters are given special values that are guaranteed not to match anything
4554 in the edge structure being shipped out. On the other hand, the initial color
4555 should be black so that the translation of an all-black picture will have no
4556 \&{setcolor} commands. (These would be undesirable in a font application.)
4557 Hence we use |c=0| when initializing the graphics state and we use |c<0|
4558 to recover from a situation where we have lost track of the graphics state.
4561 void mp_gs_unknown_graphics_state (MP mp,scaled c) ;
4564 @d mp_void (null+1) /* a null pointer different from |null| */
4566 @c void mp_gs_unknown_graphics_state (MP mp,scaled c) {
4567 struct _gs_state *p; /* to shift graphic states around */
4568 if ( (c==0)||(c==-1) ) {
4569 if ( mp->ps->gs_state==NULL ) {
4570 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4573 while ( gs_previous!=NULL ) {
4575 mp_xfree(mp->ps->gs_state);
4579 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
4580 gs_colormodel=mp_uninitialized_model;
4587 } else if ( c==1 ) {
4588 p= mp->ps->gs_state;
4589 mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
4590 memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
4592 } else if ( c==2 ) {
4594 mp_xfree(mp->ps->gs_state);
4600 @ When it is time to output a graphical object, |fix_graphics_state| ensures
4601 that \ps's idea of the graphics state agrees with what is stored in the object.
4604 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) ;
4607 void mp_gr_fix_graphics_state (MP mp, struct mp_graphic_object *p) {
4608 /* get ready to output graphical object |p| */
4609 struct mp_knot *pp; /* for list manipulation */
4610 struct mp_dash_list *hh;
4611 scaled wx,wy,ww; /* dimensions of pen bounding box */
4612 boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
4613 integer tx,ty; /* temporaries for computing |adj_wx| */
4614 scaled scf; /* a scale factor for the dash pattern */
4615 if ( gr_has_color(p) )
4616 @<Make sure \ps\ will use the right color for object~|p|@>;
4617 if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) )
4618 if ( gr_pen_p(p)!=null )
4619 if ( pen_is_elliptical(gr_pen_p(p)) ) {
4620 @<Generate \ps\ code that sets the stroke width to the
4621 appropriate rounded value@>;
4622 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
4623 @<Decide whether the line cap parameter matters and set it if necessary@>;
4624 @<Set the other numeric parameters as needed for object~|p|@>;
4626 if ( mp->ps_offset>0 ) mp_print_ln(mp);
4629 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
4630 if ( gr_type(p)==mp_stroked_code )
4631 if ( (gr_left_type(gr_path_p(p))==mp_endpoint)||(gr_dash_p(p)!=NULL) )
4632 if ( gs_lcap!=gr_lcap_val(p) ) {
4634 mp_print_char(mp, ' ');
4635 mp_print_char(mp, '0'+gr_lcap_val(p));
4636 mp_ps_print_cmd(mp, " setlinecap"," lc");
4637 gs_lcap=gr_lcap_val(p);
4640 @ @<Set the other numeric parameters as needed for object~|p|@>=
4641 if ( gs_ljoin!=gr_ljoin_val(p) ) {
4643 mp_print_char(mp, ' ');
4644 mp_print_char(mp, '0'+gr_ljoin_val(p));
4645 mp_ps_print_cmd(mp, " setlinejoin"," lj");
4646 gs_ljoin=gr_ljoin_val(p);
4648 if ( gs_miterlim!=gr_miterlim_val(p) ) {
4650 mp_print_char(mp, ' ');
4651 mp_print_scaled(mp, gr_miterlim_val(p));
4652 mp_ps_print_cmd(mp, " setmiterlimit"," ml");
4653 gs_miterlim=gr_miterlim_val(p);
4656 @ @<Make sure \ps\ will use the right color for object~|p|@>=
4658 if ( (gr_color_model(p)==mp_rgb_model)||
4659 ((gr_color_model(p)==mp_uninitialized_model)&&
4660 ((mp->internal[mp_default_color_model]>>16)==mp_rgb_model)) ) {
4661 if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=gr_red_val(p))||
4662 (gs_green!=gr_green_val(p))||(gs_blue!=gr_blue_val(p)) ) {
4663 gs_red=gr_red_val(p);
4664 gs_green=gr_green_val(p);
4665 gs_blue=gr_blue_val(p);
4667 gs_colormodel=mp_rgb_model;
4669 mp_print_char(mp, ' ');
4670 mp_print_scaled(mp, gs_red); mp_print_char(mp, ' ');
4671 mp_print_scaled(mp, gs_green); mp_print_char(mp, ' ');
4672 mp_print_scaled(mp, gs_blue);
4673 mp_ps_print_cmd(mp, " setrgbcolor", " R");
4676 } else if ( (gr_color_model(p)==mp_cmyk_model)||
4677 ((gr_color_model(p)==mp_uninitialized_model)&&
4678 ((mp->internal[mp_default_color_model]>>16)==mp_cmyk_model)) ) {
4679 if ( (gs_red!=gr_cyan_val(p))||(gs_green!=gr_magenta_val(p))||
4680 (gs_blue!=gr_yellow_val(p))||(gs_black!=gr_black_val(p))||
4681 (gs_colormodel!=mp_cmyk_model) ) {
4682 if ( gr_color_model(p)==mp_uninitialized_model ) {
4688 gs_red=gr_cyan_val(p);
4689 gs_green=gr_magenta_val(p);
4690 gs_blue=gr_yellow_val(p);
4691 gs_black=gr_black_val(p);
4693 gs_colormodel=mp_cmyk_model;
4695 mp_print_char(mp, ' ');
4696 mp_print_scaled(mp, gs_red); mp_print_char(mp, ' ');
4697 mp_print_scaled(mp, gs_green); mp_print_char(mp, ' ');
4698 mp_print_scaled(mp, gs_blue); mp_print_char(mp, ' ');
4699 mp_print_scaled(mp, gs_black);
4700 mp_ps_print_cmd(mp, " setcmykcolor"," C");
4703 } else if ( (gr_color_model(p)==mp_grey_model)||
4704 ((gr_color_model(p)==mp_uninitialized_model)&&
4705 ((mp->internal[mp_default_color_model]>>16)==mp_grey_model)) ) {
4706 if ( (gs_red!=gr_grey_val(p))||(gs_colormodel!=mp_grey_model) ) {
4707 gs_red = gr_grey_val(p);
4711 gs_colormodel=mp_grey_model;
4713 mp_print_char(mp, ' ');
4714 mp_print_scaled(mp, gs_red);
4715 mp_ps_print_cmd(mp, " setgray"," G");
4719 if ( gr_color_model(p)==mp_no_model )
4720 gs_colormodel=mp_no_model;
4723 @ In order to get consistent widths for horizontal and vertical pen strokes, we
4724 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
4725 @:setwidth}{\&{setwidth}command@>
4726 We set |gs_width| to the ideal horizontal or vertical stroke width and then
4727 generate \ps\ code that computes the rounded value. For non-circular pens, the
4728 pen shape will be rescaled so that horizontal or vertical parts of the stroke
4729 have the computed width.
4731 Rounding the width to whole pixels is not likely to improve the appearance of
4732 diagonal or curved strokes, but we do it anyway for consistency. The
4733 \&{truncate} command generated here tends to make all the strokes a little
4734 @:truncate}{\&{truncate} command@>
4735 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
4736 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
4737 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
4740 @<Generate \ps\ code that sets the stroke width...@>=
4741 @<Set |wx| and |wy| to the width and height of the bounding box for
4743 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
4744 important and set |adj_wx| and |ww| accordingly@>;
4745 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
4748 mp_print_char(mp, ' '); mp_print_scaled(mp, ww);
4750 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
4752 if ( mp->internal[mp_procset]>0 ) {
4754 mp_print_char(mp, ' ');
4755 mp_print_scaled(mp, ww);
4756 mp_ps_print(mp, " vlw");
4759 mp_print(mp, " 0 "); mp_print_scaled(mp, ww);
4760 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
4767 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
4769 if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
4770 wx = abs(gr_left_x(pp) - gr_x_coord(pp));
4771 wy = abs(gr_right_y(pp) - gr_y_coord(pp));
4773 wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
4774 wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
4777 @ The path is considered ``essentially horizontal'' if its range of
4778 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
4779 vertical'' paths are detected similarly. This code ensures that no component
4780 of the pen transformation is more that |aspect_bound*(ww+1)|.
4782 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
4783 more than this factor */
4788 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
4790 if ( mp_gr_coord_rangeOK(gr_path_p(p), do_y_loc, wy) ) tx=aspect_bound;
4791 else if ( mp_gr_coord_rangeOK(gr_path_p(p), do_x_loc, wx) ) ty=aspect_bound;
4792 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
4793 else { ww=wx; adj_wx=true; }
4795 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
4796 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
4797 allowable range for $x$ or~$y$. We do not need and cannot afford a full
4798 bounding-box computation.
4801 boolean mp_gr_coord_rangeOK (struct mp_knot *h,
4802 small_number zoff, scaled dz);
4805 boolean mp_gr_coord_rangeOK (struct mp_knot *h,
4806 small_number zoff, scaled dz) {
4807 struct mp_knot *p; /* for scanning the path form |h| */
4808 scaled zlo,zhi; /* coordinate range so far */
4809 scaled z; /* coordinate currently being tested */
4810 if (zoff==do_x_loc) {
4814 while ( gr_right_type(p)!=mp_endpoint ) {
4816 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4817 p=gr_next_knot(p); z=gr_left_x(p);
4818 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4820 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4827 while ( gr_right_type(p)!=mp_endpoint ) {
4829 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4830 p=gr_next_knot(p); z=gr_left_y(p);
4831 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4833 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
4840 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
4842 else if ( z>zhi ) zhi=z;
4843 if ( zhi-zlo>dz ) return false
4845 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
4846 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
4847 @:stroke}{\&{stroke} command@>
4848 @:fill}{\&{fill} command@>
4849 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
4852 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
4853 if ( gr_type(p)==mp_fill_code ) {
4857 scf=mp_gr_get_pen_scale(mp, gr_pen_p(p));
4859 if ( gs_width==0 ) scf=gr_dash_scale(p); else hh=NULL;
4861 scf=mp_make_scaled(mp, gs_width,scf);
4862 scf=mp_take_scaled(mp, scf,gr_dash_scale(p));
4866 if ( gs_dash_p!=NULL ) {
4867 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4870 } else if ( (gs_dash_sc!=scf) || ! mp_gr_same_dashes(gs_dash_p,hh) ) {
4871 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
4875 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) ;
4879 scaled mp_gr_get_pen_scale (MP mp, struct mp_knot *p) {
4880 return mp_sqrt_det(mp,
4881 gr_left_x(p)-gr_x_coord(p), gr_right_x(p)-gr_x_coord(p),
4882 gr_left_y(p)-gr_y_coord(p), gr_right_y(p)-gr_y_coord(p));
4886 @ Translating a dash list into \ps\ is very similar to printing it symbolically
4887 in |print_edges|. A dash pattern with |dash_y(hh)=0| has length zero and is
4888 ignored. The same fate applies in the bizarre case of a dash pattern that
4889 cannot be printed without overflow.
4891 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
4894 if ( (gr_dash_y(hh)==0) ||
4895 ((abs(gr_dash_y(hh)) / unity) >= (el_gordo / scf))) {
4896 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
4898 struct mp_dash_item *dpp=gr_dash_list(hh);
4901 while ( dpp!=NULL ) {
4902 mp_ps_pair_out(mp, mp_take_scaled(mp, gr_stop_x(dpp)-gr_start_x(dpp),scf),
4903 mp_take_scaled(mp, gr_start_x(gr_dash_link(dpp))-gr_stop_x(dpp),scf));
4904 dpp=gr_dash_link(dpp);
4908 mp_print_scaled(mp, mp_take_scaled(mp, mp_gr_dash_offset(mp, hh),scf));
4909 mp_ps_print_cmd(mp, " setdash"," sd");
4914 boolean mp_gr_same_dashes (struct mp_dash_list *h, struct mp_dash_list *hh) ;
4917 boolean mp_gr_same_dashes (struct mp_dash_list * h, struct mp_dash_list *hh) {
4918 /* do |h| and |hh| represent the same dash pattern? */
4919 struct mp_dash_item * p, *pp; /* dash nodes being compared */
4920 if ( h==hh ) return true;
4921 else if ( (h==NULL)||(hh==NULL) ) return false;
4922 else if ( gr_dash_y(h)!=gr_dash_y(hh) ) return false;
4923 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
4924 return false; /* can't happen */
4927 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
4928 { p=gr_dash_list(h);
4929 pp=gr_dash_list(hh);
4930 while ( (p!=NULL)&&(pp!=NULL) ) {
4931 if ( (gr_start_x(p)!=gr_start_x(pp))||
4932 (gr_stop_x(p)!=gr_stop_x(pp)) ) {
4936 pp=gr_dash_link(pp);
4943 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) ;
4946 scaled mp_gr_dash_offset (MP mp, struct mp_dash_list *h) {
4947 scaled x; /* the answer */
4948 if ( (gr_dash_list(h)==NULL) || (gr_dash_y(h)<0) )
4949 mp_confusion(mp, "dash0");
4950 @:this can't happen dash0}{\quad dash0@>
4951 if ( gr_dash_y(h)==0 ) {
4954 x=-(gr_start_x(gr_dash_list(h)) % gr_dash_y(h));
4955 if ( x<0 ) x=x+gr_dash_y(h);
4960 @ When stroking a path with an elliptical pen, it is necessary to transform
4961 the coordinate system so that a unit circular pen will have the desired shape.
4962 To keep this transformation local, we enclose it in a
4963 $$\&{gsave}\ldots\&{grestore}$$
4964 block. Any translation component must be applied to the path being stroked
4965 while the rest of the transformation must apply only to the pen.
4966 If |fill_also=true|, the path is to be filled as well as stroked so we must
4967 insert commands to do this after giving the path.
4970 void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) ;
4973 @c void mp_gr_stroke_ellipse (MP mp, struct mp_graphic_object *h, boolean fill_also) {
4974 /* generate an elliptical pen stroke from object |h| */
4975 scaled txx,txy,tyx,tyy; /* transformation parameters */
4976 struct mp_knot *p; /* the pen to stroke with */
4977 scaled d1,det; /* for tweaking transformation parameters */
4978 integer s; /* also for tweaking transformation paramters */
4979 boolean transformed; /* keeps track of whether gsave/grestore are needed */
4981 @<Use |pen_p(h)| to set the transformation parameters and give the initial
4983 @<Tweak the transformation parameters so the transformation is nonsingular@>;
4984 mp_gr_ps_path_out(mp, gr_path_p(h));
4985 if ( mp->internal[mp_procset]==0 ) {
4986 if ( fill_also ) mp_print_nl(mp, "gsave fill grestore");
4987 @<Issue \ps\ commands to transform the coordinate system@>;
4988 mp_ps_print(mp, " stroke");
4989 if ( transformed ) mp_ps_print(mp, " grestore");
4991 if ( fill_also ) mp_print_nl(mp, "B"); else mp_print_ln(mp);
4992 if ( (txy!=0)||(tyx!=0) ) {
4994 mp_ps_pair_out(mp, txx,tyx);
4995 mp_ps_pair_out(mp, txy,tyy);
4996 mp_ps_print(mp, "0 0] t");
4997 } else if ((txx!=unity)||(tyy!=unity) ) {
4998 mp_ps_pair_out(mp,txx,tyy);
5001 mp_ps_print(mp, " S");
5002 if ( transformed ) mp_ps_print(mp, " Q");
5007 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
5013 if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
5014 mp_print_nl(mp, "");
5015 mp_ps_print_cmd(mp, "gsave ","q ");
5016 mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
5017 mp_ps_print(mp, "translate ");
5024 mp_print_nl(mp, "");
5026 @<Adjust the transformation to account for |gs_width| and output the
5027 initial \&{gsave} if |transformed| should be |true|@>
5029 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
5030 if ( gs_width!=unity ) {
5031 if ( gs_width==0 ) {
5032 txx=unity; tyy=unity;
5034 txx=mp_make_scaled(mp, txx,gs_width);
5035 txy=mp_make_scaled(mp, txy,gs_width);
5036 tyx=mp_make_scaled(mp, tyx,gs_width);
5037 tyy=mp_make_scaled(mp, tyy,gs_width);
5040 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
5041 if ( (! transformed) ){
5042 mp_ps_print_cmd(mp, "gsave ","q ");
5047 @ @<Issue \ps\ commands to transform the coordinate system@>=
5048 if ( (txy!=0)||(tyx!=0) ){
5050 mp_print_char(mp, '[');
5051 mp_ps_pair_out(mp, txx,tyx);
5052 mp_ps_pair_out(mp, txy,tyy);
5053 mp_ps_print(mp, "0 0] concat");
5054 } else if ( (txx!=unity)||(tyy!=unity) ){
5056 mp_ps_pair_out(mp, txx,tyy);
5057 mp_print(mp, "scale");
5060 @ The \ps\ interpreter will probably abort if it encounters a singular
5061 transformation matrix. The determinant must be large enough to ensure that
5062 the printed representation will be nonsingular. Since the printed
5063 representation is always within $2^{-17}$ of the internal |scaled| value, the
5064 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
5065 the magnitudes of |txx/65536|, |txy/65536|, etc.
5067 The |aspect_bound*(gs_width+1)| bound on the components of the pen
5068 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
5070 @<Tweak the transformation parameters so the transformation is nonsingular@>=
5071 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
5072 d1=4*aspect_bound+1;
5073 if ( abs(det)<d1 ) {
5074 if ( det>=0 ) { d1=d1-det; s=1; }
5075 else { d1=-d1-det; s=-1; };
5077 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
5078 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
5079 else txx=txx+(d1+s*abs(tyy)) / tyy;
5081 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
5082 else txy=txy+(d1+s*abs(tyx)) / tyx;
5086 @ Here is a simple routine that just fills a cycle.
5089 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p);
5092 void mp_gr_ps_fill_out (MP mp, struct mp_knot *p) { /* fill cyclic path~|p| */
5093 mp_gr_ps_path_out(mp, p);
5094 mp_ps_print_cmd(mp, " fill"," F");
5098 @ A text node may specify an arbitrary transformation but the usual case
5099 involves only shifting, scaling, and occasionally rotation. The purpose
5100 of |choose_scale| is to select a scale factor so that the remaining
5101 transformation is as ``nice'' as possible. The definition of ``nice''
5102 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
5103 nice because they work out well for bitmap fonts. The code here selects
5104 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
5105 non-shifting part of the transformation matrix. It is careful to avoid
5106 additions that might cause undetected overflow.
5109 scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) ;
5111 @ @c scaled mp_gr_choose_scale (MP mp, struct mp_graphic_object *p) {
5112 /* |p| should point to a text node */
5113 scaled a,b,c,d,ad,bc; /* temporary values */
5118 if ( (a<0) ) negate(a);
5119 if ( (b<0) ) negate(b);
5120 if ( (c<0) ) negate(c);
5121 if ( (d<0) ) negate(d);
5124 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
5129 @d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
5132 void mp_gr_ship_out (MP mp, struct mp_graphic_object *h) {
5133 struct mp_graphic_object *p;
5134 scaled ds,scf; /* design size and scale factor for a text node */
5135 boolean transformed; /* is the coordinate system being transformed? */
5138 if ( gr_has_color(p) ) {
5139 if ( (gr_pre_script(p))!=NULL ) {
5140 mp_print_nl (mp, gr_pre_script(p));
5144 mp_gr_fix_graphics_state(mp, p);
5145 switch (gr_type(p)) {
5147 if ( gr_pen_p(p)==NULL ) mp_gr_ps_fill_out(mp, gr_path_p(p));
5148 else if ( pen_is_elliptical(gr_pen_p(p)) ) mp_gr_stroke_ellipse(mp, p,true);
5150 /* NOTE: these have to be the result of
5151 |mp_make_envelope(mp, mp_copy_path(path_p(p),p ...))|
5153 |mp_make_envelope(mp, mp_htap_ypoc(path_p(p),p ...))|
5155 mp_gr_ps_fill_out(mp, gr_path_p(p));
5156 mp_gr_ps_fill_out(mp, gr_htap_p(p));
5158 if ( gr_post_script(p)!=NULL ) {
5159 mp_print_nl (mp, gr_post_script(p)); mp_print_ln(mp);
5162 case mp_stroked_code:
5163 if ( pen_is_elliptical(gr_pen_p(p)) ) mp_gr_stroke_ellipse(mp, p,false);
5165 /* NOTE: this has to be the result of :
5166 |q=mp_gr_copy_path(mp, gr_path_p(p));|
5168 |@<Break the cycle and set |t:=1| if path |q| is cyclic@>;|
5169 |q=mp_make_envelope(mp, q, pen_p(p),ljoin_val(p),t,miterlim_val(p));|
5171 mp_gr_ps_fill_out(mp, gr_path_p(p));
5173 if ( gr_post_script(p)!=NULL ) {
5174 mp_print_nl (mp, gr_post_script(p)); mp_print_ln(mp);
5178 if ( (gr_font_n(p)!=null_font) && (strlen(gr_text_p(p))>0) ) {
5179 if ( mp->internal[mp_prologues]>0 )
5180 scf=mp_gr_choose_scale(mp, p);
5182 scf=mp_indexed_size(mp, gr_font_n(p), gr_name_type(p));
5183 @<Shift or transform as necessary before outputting text node~|p| at scale
5184 factor~|scf|; set |transformed:=true| if the original transformation must
5186 mp_ps_string_out(mp, gr_text_p(p));
5187 mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
5188 @<Print the size information and \ps\ commands for text node~|p|@>;
5191 if ( gr_post_script(p)!=NULL ) {
5192 mp_print_nl (mp, gr_post_script(p)); mp_print_ln(mp);
5195 case mp_start_clip_code:
5196 mp_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
5197 mp_gr_ps_path_out(mp, gr_path_p(p));
5198 mp_ps_print_cmd(mp, " clip"," W");
5200 if ( mp->internal[mp_restore_clip_color]>0 )
5201 mp_gs_unknown_graphics_state(mp, 1);
5203 case mp_stop_clip_code:
5204 mp_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
5206 if ( mp->internal[mp_restore_clip_color]>0 )
5207 mp_gs_unknown_graphics_state(mp, 2);
5209 mp_gs_unknown_graphics_state(mp, -1);
5211 case mp_start_bounds_code:
5212 case mp_stop_bounds_code:
5214 } /* all cases are enumerated */
5219 @ The envelope of a cyclic path~|q| could be computed by calling
5220 |make_envelope| once for |q| and once for its reversal. We don't do this
5221 because it would fail color regions that are covered by the pen regardless
5222 of where it is placed on~|q|.
5224 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
5225 if ( gr_left_type(q)!=mp_endpoint ) {
5226 gr_left_type(mp_gr_insert_knot(mp, q,gr_x_coord(q),gr_y_coord(q)))=mp_endpoint;
5227 gr_right_type(q)=mp_endpoint;
5232 @ @<Print the size information and \ps\ commands for text node~|p|@>=
5234 mp_print_char(mp, ' ');
5235 ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
5236 mp_print_scaled(mp, mp_take_scaled(mp, ds,scf));
5237 mp_print(mp, " fshow");
5239 mp_ps_print_cmd(mp, " grestore"," Q")
5243 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
5244 transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
5245 (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
5246 if ( transformed ) {
5247 mp_ps_print_cmd(mp, "gsave [", "q [");
5248 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
5249 mp_make_scaled(mp, gr_tyx_val(p),scf));
5250 mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
5251 mp_make_scaled(mp, gr_tyy_val(p),scf));
5252 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5253 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
5255 mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
5256 mp_ps_print_cmd(mp, "moveto","m");