summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/mplibdir/psout.w
diff options
context:
space:
mode:
authorTaco Hoekwater <taco@elvenkind.com>2009-04-16 07:36:46 +0000
committerTaco Hoekwater <taco@elvenkind.com>2009-04-16 07:36:46 +0000
commit79e9c535970a1887e1b6b2196cd90775955ea486 (patch)
tree61c8bd011c749b0d0e732630e69bb3f3f07b7219 /Build/source/texk/web2c/mplibdir/psout.w
parent9a146db3a4562660570a744a7c7471132090a537 (diff)
This is an initial import of the current trunk state of MPlib
(version 1.110 and a few minor patches). For the moment, the mplib.am builds the executable "newmpost", that should be fixed soon. git-svn-id: svn://tug.org/texlive/trunk@12732 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/mplibdir/psout.w')
-rw-r--r--Build/source/texk/web2c/mplibdir/psout.w6246
1 files changed, 6246 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mplibdir/psout.w b/Build/source/texk/web2c/mplibdir/psout.w
new file mode 100644
index 00000000000..90bc96a9aa3
--- /dev/null
+++ b/Build/source/texk/web2c/mplibdir/psout.w
@@ -0,0 +1,6246 @@
+% $Id: psout.w 878 2009-03-22 08:22:33Z taco $
+%
+% Copyright 2008 Taco Hoekwater.
+%
+% This program is free software: you can redistribute it and/or modify
+% it under the terms of the GNU Lesser General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% This program is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU Lesser General Public License for more details.
+%
+% You should have received a copy of the GNU Lesser General Public License
+% along with this program. If not, see <http://www.gnu.org/licenses/>.
+%
+% TeX is a trademark of the American Mathematical Society.
+% METAFONT is a trademark of Addison-Wesley Publishing Company.
+% PostScript is a trademark of Adobe Systems Incorporated.
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
+\def\PASCAL{Pascal}
+\def\ps{PostScript}
+\def\ph{\hbox{Pascal-H}}
+\def\psqrt#1{\sqrt{\mathstrut#1}}
+\def\k{_{k+1}}
+\def\pct!{{\char`\%}} % percent sign in ordinary text
+\font\tenlogo=logo10 % font used for the METAFONT logo
+\font\logos=logosl10
+\def\MF{{\tenlogo META}\-{\tenlogo FONT}}
+\def\MP{{\tenlogo META}\-{\tenlogo POST}}
+\def\<#1>{$\langle#1\rangle$}
+\def\section{\mathhexbox278}
+\let\swap=\leftrightarrow
+\def\round{\mathop{\rm round}\nolimits}
+\mathchardef\vbv="026A % synonym for `\|'
+\def\vb{\relax\ifmmode\vbv\else$\vbv$\fi}
+\def\[#1]{} % from pascal web
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
+
+\let\?=\relax % we want to be able to \write a \?
+
+\def\title{MetaPost \ps\ output}
+\def\topofcontents{\hsize 5.5in
+ \vglue -30pt plus 1fil minus 1.5in
+ \def\?##1]{\hbox to 1in{\hfil##1.\ }}
+ }
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\pdfoutput=1
+\pageno=3
+
+@
+@d true 1
+@d false 0
+@d null_font 0
+@d null 0
+@d unity 0200000 /* $2^{16}$, represents 1.00000 */
+@d el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
+@d incr(A) (A)=(A)+1 /* increase a variable by unity */
+@d decr(A) (A)=(A)-1 /* decrease a variable by unity */
+@d negate(A) (A)=-(A) /* change the sign of a variable */
+@d odd(A) ((A)%2==1)
+@d half(A) ((A)/2)
+@d print_err(A) mp_print_err(mp,(A))
+@d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
+
+@c
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+#include "avl.h"
+#include "mplib.h"
+#include "mplibps.h" /* external header */
+#include "mpmp.h" /* internal header */
+#include "mppsout.h" /* internal header */
+@h
+@<Declarations@>
+@<Static variables in the outer block@>
+
+@ There is a small bit of code from the backend that bleads through
+to the frontend because I do not know how to set up the includes
+properly. That is the |typedef struct psout_data_struct * psout_data|.
+
+@ @(mppsout.h@>=
+#include "avl.h"
+@<Types...@>
+typedef struct psout_data_struct {
+ @<Globals@>
+} psout_data_struct ;
+@<Exported function headers@>
+
+@
+@c
+void *avl_probe (avl_tree t, void *p) {
+ int ret = avl_ins(p, t, false);
+ if (ret < 0)
+ return NULL;
+ else
+ return avl_find(p, t);
+}
+
+@ @<Exported...@>=
+void *avl_probe (avl_tree t, void *p) ;
+
+
+@ @c
+static boolean mp_isdigit (int a) {
+ return (a>='0'&&a<='9');
+}
+static int mp_tolower (int a) {
+ if (a>='A' && a <='Z')
+ return a - 'A' + 'a';
+ return a;
+}
+static int mp_strcasecmp (const char *s1, const char *s2) {
+ int r;
+ char *ss1, *ss2, *c;
+ ss1 = mp_strdup(s1);
+ c = ss1;
+ while (*c != '\0') {
+ *c = mp_tolower(*c); c++;
+ }
+ ss2 = mp_strdup(s2);
+ c = ss2;
+ while (*c != '\0') {
+ *c = mp_tolower(*c); c++;
+ }
+ r = strcmp(ss1,ss2);
+ free (ss1); free(ss2);
+ return r;
+}
+
+@ @<Exported function headers@>=
+void mp_ps_backend_initialize (MP mp) ;
+void mp_ps_backend_free (MP mp) ;
+
+@
+@c void mp_ps_backend_initialize (MP mp) {
+ mp->ps = mp_xmalloc(mp,1,sizeof(psout_data_struct));
+ @<Set initial values@>;
+}
+void mp_ps_backend_free (MP mp) {
+ @<Dealloc variables@>;
+ enc_free(mp);
+ t1_free(mp);
+ fm_free(mp);
+ mp_xfree(mp->ps);
+ mp->ps = NULL;
+}
+
+@ Writing to ps files
+
+@<Globals@>=
+integer ps_offset;
+ /* the number of characters on the current \ps\ file line */
+
+@ @<Set initial values@>=
+mp->ps->ps_offset = 0;
+
+@
+
+@d wps(A) (mp->write_ascii_file)(mp,mp->output_file,(A))
+@d wps_chr(A) do {
+ char ss[2];
+ ss[0]=(A); ss[1]=0;
+ (mp->write_ascii_file)(mp,mp->output_file,(char *)ss);
+} while (0)
+@d wps_cr (mp->write_ascii_file)(mp,mp->output_file,"\n")
+@d wps_ln(A) { wterm_cr; (mp->write_ascii_file)(mp,mp->output_file,(A)); }
+
+@c
+static void mp_ps_print_ln (MP mp) { /* prints an end-of-line */
+ wps_cr;
+ mp->ps->ps_offset=0;
+}
+
+@ @c
+static void mp_ps_print_char (MP mp, int s) { /* prints a single character */
+ if ( s==13 ) {
+ wps_cr; mp->ps->ps_offset=0;
+ } else {
+ wps_chr(s); incr(mp->ps->ps_offset);
+ }
+}
+
+@ @c
+static void mp_ps_do_print (MP mp, const char *ss, size_t len) { /* prints string |s| */
+ size_t j = 0;
+ while ( j<len ){
+ mp_ps_print_char(mp, ss[j]); incr(j);
+ }
+}
+
+@ Deciding where to break the ps output line.
+
+@d ps_room(A) if (mp->ps->ps_offset>0 && (mp->ps->ps_offset+(int)(A))>mp->max_print_line ) {
+ mp_ps_print_ln(mp); /* optional line break */
+}
+
+@c
+static void mp_ps_print (MP mp, const char *ss) {
+ ps_room(strlen(ss));
+ mp_ps_do_print(mp, ss, strlen(ss));
+}
+
+@ The procedure |print_nl| is like |print|, but it makes sure that the
+string appears at the beginning of a new line.
+
+@c
+static void mp_ps_print_nl (MP mp, const char *s) { /* prints string |s| at beginning of line */
+ if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
+ mp_ps_print(mp, s);
+}
+
+@ An array of digits in the range |0..9| is printed by |print_the_digs|.
+
+@c
+static void mp_ps_print_the_digs (MP mp, int k) {
+ /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
+ while ( k-->0 ){
+ mp_ps_print_char(mp, '0'+mp->dig[k]);
+ }
+}
+
+@ The following procedure, which prints out the decimal representation of a
+given integer |n|, has been written carefully so that it works properly
+if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
+to negative arguments, since such operations are not implemented consistently
+by all \PASCAL\ compilers.
+
+@c
+static void mp_ps_print_int (MP mp,integer n) { /* prints an integer in decimal form */
+ integer m; /* used to negate |n| in possibly dangerous cases */
+ int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
+ if ( n<0 ) {
+ mp_ps_print_char(mp, '-');
+ if ( n>-100000000 ) {
+ negate(n);
+ } else {
+ m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
+ if ( m<10 ) {
+ mp->dig[0]=(unsigned char)m;
+ } else {
+ mp->dig[0]=0; incr(n);
+ }
+ }
+ }
+ do {
+ mp->dig[k]=(unsigned char)(n % 10); n=n / 10; incr(k);
+ } while (n!=0);
+ mp_ps_print_the_digs(mp, k);
+}
+
+@ \MP\ also makes use of a trivial procedure to print two digits. The
+following subroutine is usually called with a parameter in the range |0<=n<=99|.
+
+@c
+static void mp_ps_print_dd (MP mp,integer n) { /* prints two least significant digits */
+ n=abs(n) % 100;
+ mp_ps_print_char(mp, '0'+(n / 10));
+ mp_ps_print_char(mp, '0'+(n % 10));
+}
+
+@ Conversely, here is a procedure analogous to |print_int|. If the output
+of this procedure is subsequently read by \MP\ and converted by the
+|round_decimals| routine above, it turns out that the original value will
+be reproduced exactly. A decimal point is printed only if the value is
+not an integer. If there is more than one way to print the result with
+the optimum number of digits following the decimal point, the closest
+possible value is given.
+
+The invariant relation in the \&{repeat} loop is that a sequence of
+decimal digits yet to be printed will yield the original number if and only if
+they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
+We can stop if and only if $f=0$ satisfies this condition; the loop will
+terminate before $s$ can possibly become zero.
+
+@c
+static void mp_ps_print_scaled (MP mp,scaled s) {
+ scaled delta; /* amount of allowable inaccuracy */
+ if ( s<0 ) {
+ mp_ps_print_char(mp, '-');
+ negate(s); /* print the sign, if negative */
+ }
+ mp_ps_print_int(mp, s / unity); /* print the integer part */
+ s=10*(s % unity)+5;
+ if ( s!=5 ) {
+ delta=10;
+ mp_ps_print_char(mp, '.');
+ do {
+ if ( delta>unity )
+ s=s+0100000-(delta / 2); /* round the final digit */
+ mp_ps_print_char(mp, '0'+(s / unity));
+ s=10*(s % unity);
+ delta=delta*10;
+ } while (s>delta);
+ }
+}
+
+
+@* \[44a] Dealing with font encodings.
+
+First, here are a few helpers for parsing files
+
+@d check_buf(size, buf_size)
+ if ((unsigned)(size) > (unsigned)(buf_size)) {
+ char S[128];
+ mp_snprintf(S,128,"buffer overflow: (%u,%u) at file %s, line %d",
+ (unsigned)(size),(unsigned)(buf_size), __FILE__, __LINE__ );
+ mp_fatal_error(mp,S);
+ }
+
+@d append_char_to_buf(c, p, buf, buf_size) do {
+ if (c == 9)
+ c = 32;
+ if (c == 13 || c == EOF)
+ c = 10;
+ if (c != ' ' || (p > buf && p[-1] != 32)) {
+ check_buf(p - buf + 1, (buf_size));
+ *p++ = c;
+ }
+} while (0)
+
+@d append_eol(p, buf, buf_size) do {
+ check_buf(p - buf + 2, (buf_size));
+ if (p - buf > 1 && p[-1] != 10)
+ *p++ = 10;
+ if (p - buf > 2 && p[-2] == 32) {
+ p[-2] = 10;
+ p--;
+ }
+ *p = 0;
+} while (0)
+
+@d remove_eol(p, buf) do {
+ p = strend(buf) - 1;
+ if (*p == 10)
+ *p = 0;
+} while (0)
+
+@d skip(p, c) if (*p == c) p++
+@d strend(s) strchr(s, 0)
+@d str_prefix(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
+
+
+@ @<Types...@>=
+typedef struct {
+ boolean loaded; /* the encoding has been loaded? */
+ char *file_name; /* encoding file name */
+ char *enc_name; /* encoding true name */
+ integer objnum; /* object number */
+ char **glyph_names;
+ integer tounicode; /* object number of associated ToUnicode entry */
+} enc_entry;
+
+
+@
+
+@d ENC_STANDARD 0
+@d ENC_BUILTIN 1
+
+@<Glob...@>=
+#define ENC_BUF_SIZE 0x1000
+char enc_line[ENC_BUF_SIZE];
+void * enc_file;
+
+@
+@d enc_eof() (mp->eof_file)(mp,mp->ps->enc_file)
+@d enc_close() (mp->close_file)(mp,mp->ps->enc_file)
+
+@c
+static int enc_getchar(MP mp) {
+ size_t len = 1;
+ unsigned char abyte=0;
+ void *byte_ptr = &abyte;
+ (mp->read_binary_file)(mp,mp->ps->enc_file,&byte_ptr,&len);
+ return abyte;
+}
+
+@ @c
+static boolean mp_enc_open (MP mp, char *n) {
+ mp->ps->enc_file=(mp->open_file)(mp,n, "r", mp_filetype_encoding);
+ if (mp->ps->enc_file!=NULL)
+ return true;
+ else
+ return false;
+}
+static void mp_enc_getline (MP mp) {
+ char *p;
+ int c;
+RESTART:
+ if (enc_eof ()) {
+ print_err("unexpected end of file");
+ mp_error(mp);
+ }
+ p = mp->ps->enc_line;
+ do {
+ c = enc_getchar (mp);
+ append_char_to_buf (c, p, mp->ps->enc_line, ENC_BUF_SIZE);
+ } while (c != 10);
+ append_eol (p, mp->ps->enc_line, ENC_BUF_SIZE);
+ if (p - mp->ps->enc_line < 2 || *mp->ps->enc_line == '%')
+ goto RESTART;
+}
+static void mp_load_enc (MP mp, char *enc_name,
+ char **enc_encname, char **glyph_names){
+ char buf[ENC_BUF_SIZE], *p, *r;
+ int names_count;
+ char *myname;
+ unsigned save_selector = mp->selector;
+ if (!mp_enc_open (mp,enc_name)) {
+ mp_print (mp,"cannot open encoding file for reading");
+ return;
+ }
+ mp_normalize_selector(mp);
+ mp_print (mp,"{");
+ mp_print (mp, enc_name);
+ mp_enc_getline (mp);
+ if (*mp->ps->enc_line != '/' || (r = strchr (mp->ps->enc_line, '[')) == NULL) {
+ remove_eol (r, mp->ps->enc_line);
+ print_err ("invalid encoding vector (a name or `[' missing): `");
+ mp_print(mp,mp->ps->enc_line);
+ mp_print(mp,"'");
+ mp_error(mp);
+ }
+ while (*(r-1)==' ') r--; /* strip trailing spaces from encoding name */
+ myname = mp_xmalloc(mp,r-mp->ps->enc_line,1);
+ memcpy(myname,mp->ps->enc_line+1,(r-mp->ps->enc_line)-1);
+ *(myname+(r-mp->ps->enc_line-1))=0;
+ *enc_encname = myname;
+ while (*r!='[') r++;
+ r++; /* skip '[' */
+ names_count = 0;
+ skip (r, ' ');
+ for (;;) {
+ while (*r == '/') {
+ for (p = buf, r++;
+ *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *p++ = *r++);
+ *p = 0;
+ skip (r, ' ');
+ if (names_count > 256) {
+ print_err ("encoding vector contains more than 256 names");
+ mp_error(mp);
+ }
+ if (mp_xstrcmp (buf, notdef) != 0)
+ glyph_names[names_count] = mp_xstrdup (mp,buf);
+ names_count++;
+ }
+ if (*r != 10 && *r != '%') {
+ if (str_prefix (r, "] def"))
+ goto DONE;
+ else {
+ remove_eol (r, mp->ps->enc_line);
+ print_err
+ ("invalid encoding vector: a name or `] def' expected: `");
+ mp_print(mp,mp->ps->enc_line);
+ mp_print(mp,"'");
+ mp_error(mp);
+ }
+ }
+ mp_enc_getline (mp);
+ r = mp->ps->enc_line;
+ }
+DONE:
+ enc_close ();
+ mp_print (mp,"}");
+ mp->selector = save_selector;
+}
+static void mp_read_enc (MP mp, enc_entry * e) {
+ if (e->loaded)
+ return;
+ e->enc_name = NULL;
+ mp_load_enc (mp,e->file_name, &e->enc_name, e->glyph_names);
+ e->loaded = true;
+}
+
+@ |write_enc| is used to write either external encoding (given in map file) or
+ internal encoding (read from the font file);
+ the 2nd argument is a pointer to the encoding entry;
+
+@c
+static void mp_write_enc (MP mp, enc_entry * e) {
+ int i;
+ size_t s, foffset;
+ char **g;
+ if (e->objnum != 0) /* the encoding has been written already */
+ return;
+ e->objnum = 1;
+ g = e->glyph_names;
+
+ mp_ps_print(mp,"\n%%%%BeginResource: encoding ");
+ mp_ps_print(mp, e->enc_name);
+ mp_ps_print_nl(mp, "/");
+ mp_ps_print(mp, e->enc_name);
+ mp_ps_print(mp, " [ ");
+ mp_ps_print_ln (mp);
+ foffset = strlen(e->file_name)+3;
+ for (i = 0; i < 256; i++) {
+ s = strlen(g[i]);
+ if (s+1+foffset>=80) {
+ mp_ps_print_ln (mp);
+ foffset = 0;
+ }
+ foffset += s+2;
+ mp_ps_print_char(mp,'/');
+ mp_ps_print(mp, g[i]);
+ mp_ps_print_char(mp,' ');
+ }
+ if (foffset>75)
+ mp_ps_print_ln (mp);
+ mp_ps_print_nl (mp,"] def\n");
+ mp_ps_print(mp,"%%%%EndResource");
+}
+
+
+@ All encoding entries go into AVL tree for fast search by name.
+
+@<Glob...@>=
+avl_tree enc_tree;
+
+@
+
+@<Static variables in the outer block@>=
+static const char notdef[] = ".notdef";
+
+@ @<Set initial...@>=
+mp->ps->enc_tree = NULL;
+
+@ @c
+static int comp_enc_entry (void *p, const void *pa, const void *pb) {
+ (void)p;
+ return strcmp (((const enc_entry *) pa)->file_name,
+ ((const enc_entry *) pb)->file_name);
+}
+static void *destroy_enc_entry (void *pa) {
+ enc_entry *p;
+ int i;
+ p = (enc_entry *) pa;
+ mp_xfree (p->file_name);
+ if (p->glyph_names != NULL)
+ for (i = 0; i < 256; i++)
+ if (p->glyph_names[i] != notdef)
+ mp_xfree (p->glyph_names[i]);
+ mp_xfree (p->glyph_names);
+ mp_xfree (p);
+}
+static void *copy_enc_entry (const void *pa) {
+ enc_entry *p, *q;
+ int i;
+ p = (enc_entry *) pa;
+ q = malloc (sizeof (enc_entry));
+ if (q!=NULL) {
+ q->loaded = p->loaded;
+ q->file_name = strdup (p->file_name);
+ q->objnum = p->objnum;
+ q->tounicode = p->tounicode;
+ q->glyph_names = malloc (256 * sizeof (char *));
+ if (p->glyph_names == NULL)
+ return NULL;
+ for (i = 0; i < 256; i++) {
+ if (p->glyph_names[i]!=NULL)
+ q->glyph_names[i] = strdup(p->glyph_names[i]);
+ else
+ q->glyph_names[i] = NULL;
+ }
+ }
+ return (void *)q;
+}
+
+static enc_entry * mp_add_enc (MP mp, char *s) {
+ int i;
+ enc_entry tmp, *p;
+ void **aa;
+ if (mp->ps->enc_tree == NULL) {
+ mp->ps->enc_tree = avl_create (comp_enc_entry,
+ copy_enc_entry,
+ destroy_enc_entry,
+ malloc, free, NULL);
+ }
+ tmp.file_name = s;
+ p = (enc_entry *) avl_find (&tmp, mp->ps->enc_tree);
+ if (p != NULL) /* encoding already registered */
+ return p;
+ p = mp_xmalloc (mp,1,sizeof (enc_entry));
+ p->loaded = false;
+ p->file_name = mp_xstrdup (mp,s);
+ p->objnum = 0;
+ p->tounicode = 0;
+ p->glyph_names = mp_xmalloc (mp,256,sizeof (char *));
+ for (i = 0; i < 256; i++)
+ p->glyph_names[i] = (char *) notdef;
+ aa = avl_probe (mp->ps->enc_tree, p);
+ return p;
+}
+
+@ cleaning up...
+
+
+@ @<Declarations@>=
+static void enc_free (MP mp);
+
+@ @c static void enc_free (MP mp) {
+ if (mp->ps->enc_tree != NULL)
+ avl_destroy (mp->ps->enc_tree);
+}
+
+@ @<Declarations@>=
+static void mp_reload_encodings (MP mp) ;
+static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) ;
+
+@ @c void mp_reload_encodings (MP mp) {
+ font_number f;
+ enc_entry *e;
+ fm_entry *fm_cur;
+ font_number lastfnum = mp->last_fnum;
+ for (f=null_font+1;f<=lastfnum;f++) {
+ if (mp->font_enc_name[f]!=NULL ) {
+ mp_xfree(mp->font_enc_name[f]);
+ mp->font_enc_name[f]=NULL;
+ }
+ if (mp_has_fm_entry (mp,f,&fm_cur)) {
+ if (fm_cur != NULL && fm_cur->ps_name != NULL &&is_reencoded (fm_cur)) {
+ e = fm_cur->encoding;
+ mp_read_enc (mp,e);
+ }
+ }
+ }
+}
+static void mp_font_encodings (MP mp, font_number lastfnum, boolean encodings_only) {
+ font_number f;
+ enc_entry *e;
+ fm_entry *fm;
+ for (f=null_font+1;f<=lastfnum;f++) {
+ if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp,f, &fm)) {
+ if (fm != NULL && (fm->ps_name != NULL)) {
+ if (is_reencoded (fm)) {
+ if (encodings_only || (!is_subsetted (fm))) {
+ e = fm->encoding;
+ mp_write_enc (mp, e);
+ /* clear for next run */
+ e->objnum = 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+@* \[44b] Parsing font map files.
+
+@d FM_BUF_SIZE 1024
+
+@<Glob...@>=
+void * fm_file;
+
+@
+@d fm_close() (mp->close_file)(mp,mp->ps->fm_file)
+@d fm_eof() (mp->eof_file)(mp,mp->ps->fm_file)
+
+@c
+static int fm_getchar(MP mp) {
+ size_t len = 1;
+ unsigned char abyte=0;
+ void *byte_ptr = &abyte;
+ (mp->read_binary_file)(mp,mp->ps->fm_file,&byte_ptr,&len);
+ if (len==0)
+ return EOF;
+ return abyte;
+}
+
+
+@ @<Types...@>=
+enum _mode { FM_DUPIGNORE, FM_REPLACE, FM_DELETE };
+enum _ltype { MAPFILE, MAPLINE };
+enum _tfmavail { TFM_UNCHECKED, TFM_FOUND, TFM_NOTFOUND };
+typedef struct mitem {
+ int mode; /* |FM_DUPIGNORE| or |FM_REPLACE| or |FM_DELETE| */
+ int type; /* map file or map line */
+ char *map_line; /* pointer to map file name or map line */
+ int lineno; /* line number in map file */
+} mapitem;
+
+@ @<Glob...@>=
+mapitem *mitem;
+fm_entry *fm_cur;
+fm_entry *loaded_tfm_found;
+fm_entry *avail_tfm_found;
+fm_entry *non_tfm_found;
+fm_entry *not_avail_tfm_found;
+
+@ @<Set initial...@>=
+mp->ps->mitem = NULL;
+
+@ @<Declarations@>=
+static const char nontfm[] = "<nontfm>";
+
+@
+@d read_field(r, q, buf) do {
+ q = buf;
+ while (*r != ' ' && *r != '\0')
+ *q++ = *r++;
+ *q = '\0';
+ skip (r, ' ');
+} while (0)
+
+@d set_field(F) do {
+ if (q > buf)
+ fm->F = mp_xstrdup(mp,buf);
+ if (*r == '\0')
+ goto DONE;
+} while (0)
+
+@d cmp_return(a, b)
+ if (a > b)
+ return 1;
+ if (a < b)
+ return -1
+
+@d do_strdup(a) (a==NULL ? NULL : strdup(a))
+
+@c
+static fm_entry *new_fm_entry (MP mp) {
+ fm_entry *fm;
+ fm = mp_xmalloc (mp,1,sizeof(fm_entry));
+ fm->tfm_name = NULL;
+ fm->ps_name = NULL;
+ fm->flags = 4;
+ fm->ff_name = NULL;
+ fm->subset_tag = NULL;
+ fm->encoding = NULL;
+ fm->tfm_num = null_font;
+ fm->tfm_avail = TFM_UNCHECKED;
+ fm->type = 0;
+ fm->slant = 0;
+ fm->extend = 0;
+ fm->ff_objnum = 0;
+ fm->fn_objnum = 0;
+ fm->fd_objnum = 0;
+ fm->charset = NULL;
+ fm->all_glyphs = false;
+ fm->links = 0;
+ fm->pid = -1;
+ fm->eid = -1;
+ return fm;
+}
+
+static void *copy_fm_entry (const void *p) {
+ fm_entry *fm, *fp;
+ fp = (fm_entry *)p;
+ fm = malloc (sizeof(fm_entry));
+ if (fm==NULL)
+ return NULL;
+ memcpy(fm, fp, sizeof(fm_entry));
+ fm->tfm_name = do_strdup(fp->tfm_name);
+ fm->ps_name = do_strdup(fp->ps_name);
+ fm->ff_name = do_strdup(fp->ff_name);
+ fm->subset_tag = do_strdup(fp->subset_tag);
+ fm->charset = do_strdup(fp->charset);
+ return (void *)fm;
+}
+
+
+static void * delete_fm_entry (void *p) {
+ fm_entry *fm = (fm_entry *)p;
+ mp_xfree (fm->tfm_name);
+ mp_xfree (fm->ps_name);
+ mp_xfree (fm->ff_name);
+ mp_xfree (fm->subset_tag);
+ mp_xfree (fm->charset);
+ mp_xfree (fm);
+}
+
+static ff_entry *new_ff_entry (MP mp) {
+ ff_entry *ff;
+ ff = mp_xmalloc (mp,1,sizeof(ff_entry));
+ ff->ff_name = NULL;
+ ff->ff_path = NULL;
+ return ff;
+}
+
+static void *copy_ff_entry (const void *p) {
+ ff_entry *ff, *fp;
+ fp = (ff_entry *)p;
+ ff = (ff_entry *)malloc (sizeof(ff_entry));
+ if (ff == NULL)
+ return NULL;
+ ff->ff_name = do_strdup(fp->ff_name);
+ ff->ff_path = do_strdup(fp->ff_path);
+ return ff;
+}
+
+static void * delete_ff_entry (void *p) {
+ ff_entry *ff = (ff_entry *)p;
+ mp_xfree (ff->ff_name);
+ mp_xfree (ff->ff_path);
+ mp_xfree (ff);
+}
+
+static char *mk_base_tfm (MP mp, char *tfmname, int *i) {
+ static char buf[SMALL_BUF_SIZE];
+ char *p = tfmname, *r = strend (p) - 1, *q = r;
+ while (q > p && mp_isdigit (*q))
+ --q;
+ if (!(q > p) || q == r || (*q != '+' && *q != '-'))
+ return NULL;
+ check_buf (q - p + 1, SMALL_BUF_SIZE);
+ strncpy (buf, p, (size_t) (q - p));
+ buf[q - p] = '\0';
+ *i = atoi (q);
+ return buf;
+}
+
+@ @<Declarations@>=
+static boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm);
+
+@ @c
+boolean mp_has_fm_entry (MP mp,font_number f, fm_entry **fm) {
+ fm_entry *res = NULL;
+ res = mp_fm_lookup (mp, f);
+ if (fm != NULL) {
+ *fm =res;
+ }
+ return (res != NULL);
+}
+
+@ @<Glob...@>=
+avl_tree tfm_tree;
+avl_tree ps_tree;
+avl_tree ff_tree;
+
+@ @<Set initial...@>=
+mp->ps->tfm_tree = NULL;
+mp->ps->ps_tree = NULL;
+mp->ps->ff_tree = NULL;
+
+@ AVL sort |fm_entry| into |tfm_tree| by |tfm_name |
+
+@c
+static int comp_fm_entry_tfm (void *p, const void *pa, const void *pb) {
+ (void)p;
+ return strcmp (((const fm_entry *) pa)->tfm_name,
+ ((const fm_entry *) pb)->tfm_name);
+}
+
+@ AVL sort |fm_entry| into |ps_tree| by |ps_name|, |slant|, and |extend|
+
+@c static int comp_fm_entry_ps (void *p, const void *pa, const void *pb) {
+ int i;
+ const fm_entry *p1 = (const fm_entry *) pa;
+ const fm_entry *p2 = (const fm_entry *) pb;
+ (void)p;
+ assert (p1->ps_name != NULL && p2->ps_name != NULL);
+ if ((i = strcmp (p1->ps_name, p2->ps_name)))
+ return i;
+ cmp_return (p1->slant, p2->slant);
+ cmp_return (p1->extend, p2->extend);
+ if (p1->tfm_name != NULL && p2->tfm_name != NULL &&
+ (i = strcmp (p1->tfm_name, p2->tfm_name)))
+ return i;
+ return 0;
+}
+
+@ AVL sort |ff_entry| into |ff_tree| by |ff_name|
+
+@c static int comp_ff_entry (void *p, const void *pa, const void *pb) {
+ (void)p;
+ return strcmp (((const ff_entry *) pa)->ff_name,
+ ((const ff_entry *) pb)->ff_name);
+}
+
+@ @c static void create_avl_trees (MP mp) {
+ if (mp->ps->tfm_tree == NULL) {
+ mp->ps->tfm_tree = avl_create (comp_fm_entry_tfm,
+ copy_fm_entry,
+ delete_fm_entry,
+ malloc, free, NULL);
+ assert (mp->ps->tfm_tree != NULL);
+ }
+ if (mp->ps->ps_tree == NULL) {
+ mp->ps->ps_tree = avl_create (comp_fm_entry_ps,
+ copy_fm_entry,
+ delete_fm_entry,
+ malloc, free, NULL);
+ assert (mp->ps->ps_tree != NULL);
+ }
+ if (mp->ps->ff_tree == NULL) {
+ mp->ps->ff_tree = avl_create (comp_ff_entry,
+ copy_ff_entry,
+ delete_ff_entry,
+ malloc, free, NULL);
+ assert (mp->ps->ff_tree != NULL);
+ }
+}
+
+@ The function |avl_do_entry| is not completely symmetrical with regards
+to |tfm_name| and |ps_name handling|, e. g. a duplicate |tfm_name| gives a
+|goto exit|, and no |ps_name| link is tried. This is to keep it compatible
+with the original version.
+
+@d LINK_TFM 0x01
+@d LINK_PS 0x02
+@d set_tfmlink(fm) ((fm)->links |= LINK_TFM)
+@d set_pslink(fm) ((fm)->links |= LINK_PS)
+@d unset_tfmlink(fm) ((fm)->links &= ~LINK_TFM)
+@d unset_pslink(fm) ((fm)->links &= ~LINK_PS)
+@d has_tfmlink(fm) ((fm)->links & LINK_TFM)
+@d has_pslink(fm) ((fm)->links & LINK_PS)
+
+@c
+static int avl_do_entry (MP mp, fm_entry * fp, int mode) {
+ fm_entry *p;
+ void *a;
+ void **aa;
+ char s[128];
+
+ /* handle |tfm_name| link */
+
+ if (strcmp (fp->tfm_name, nontfm)) {
+ p = (fm_entry *) avl_find (fp, mp->ps->tfm_tree);
+ if (p != NULL) {
+ if (mode == FM_DUPIGNORE) {
+ mp_snprintf(s,128,"fontmap entry for `%s' already exists, duplicates ignored",
+ fp->tfm_name);
+ mp_warn(mp,s);
+ goto exit;
+ } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
+ if (mp_has_font_size(mp,p->tfm_num)) {
+ mp_snprintf(s,128,
+ "fontmap entry for `%s' has been used, replace/delete not allowed",
+ fp->tfm_name);
+ mp_warn(mp,s);
+ goto exit;
+ }
+ (void) avl_del (p,mp->ps->tfm_tree,&a);
+ assert (a != NULL);
+ unset_tfmlink (p);
+ if (!has_pslink (p))
+ delete_fm_entry (p);
+ }
+ }
+ if (mode != FM_DELETE) {
+ aa = avl_probe (mp->ps->tfm_tree, fp);
+ assert (aa != NULL);
+ set_tfmlink (fp);
+ }
+ }
+
+ /* handle |ps_name| link */
+
+ if (fp->ps_name != NULL) {
+ assert (fp->tfm_name != NULL);
+ p = (fm_entry *) avl_find (fp, mp->ps->ps_tree);
+ if (p != NULL) {
+ if (mode == FM_DUPIGNORE) {
+ mp_snprintf(s,128,
+ "ps_name entry for `%s' already exists, duplicates ignored",
+ fp->ps_name);
+ mp_warn(mp,s);
+ goto exit;
+ } else { /* mode == |FM_REPLACE| / |FM_DELETE| */
+ if (mp_has_font_size(mp,p->tfm_num)) {
+ /* REPLACE/DELETE not allowed */
+ mp_snprintf(s,128,
+ "fontmap entry for `%s' has been used, replace/delete not allowed",
+ p->tfm_name);
+ mp_warn(mp,s);
+ goto exit;
+ }
+ (void)avl_del (p,mp->ps->ps_tree,&a);
+ assert (a != NULL);
+ unset_pslink (p);
+ if (!has_tfmlink (p))
+ delete_fm_entry (p);
+ }
+ }
+ if (mode != FM_DELETE) {
+ aa = avl_probe (mp->ps->ps_tree, fp);
+ assert (aa != NULL);
+ set_pslink (fp);
+ }
+ }
+ exit:
+ if (!has_tfmlink (fp) && !has_pslink (fp)) /* e. g. after |FM_DELETE| */
+ return 1; /* deallocation of |fm_entry| structure required */
+ else
+ return 0;
+}
+
+@ consistency check for map entry, with warn flag
+
+@c
+static int check_fm_entry (MP mp, fm_entry * fm, boolean warn) {
+ int a = 0;
+ char s[128];
+ assert (fm != NULL);
+ if (fm->ps_name != NULL) {
+ if (is_basefont (fm)) {
+ if (is_fontfile (fm) && !is_included (fm)) {
+ if (warn) {
+ mp_snprintf(s,128, "invalid entry for `%s': "
+ "font file must be included or omitted for base fonts",
+ fm->tfm_name);
+ mp_warn(mp,s);
+ }
+ a += 1;
+ }
+ } else { /* not a base font */
+ /* if no font file given, drop this entry */
+ /* |if (!is_fontfile (fm)) {
+ if (warn) {
+ mp_snprintf(s,128,
+ "invalid entry for `%s': font file missing",
+ fm->tfm_name);
+ mp_warn(mp,s);
+ }
+ a += 2;
+ }|
+ */
+ }
+ }
+ if (is_truetype (fm) && is_reencoded (fm) && !is_subsetted (fm)) {
+ if (warn) {
+ mp_snprintf(s,128,
+ "invalid entry for `%s': only subsetted TrueType font can be reencoded",
+ fm->tfm_name);
+ mp_warn(mp,s);
+ }
+ a += 4;
+ }
+ if ((fm->slant != 0 || fm->extend != 0) &&
+ (is_truetype (fm))) {
+ if (warn) {
+ mp_snprintf(s,128,
+ "invalid entry for `%s': "
+ "SlantFont/ExtendFont can be used only with embedded T1 fonts",
+ fm->tfm_name);
+ mp_warn(mp,s);
+ }
+ a += 8;
+ }
+ if (abs (fm->slant) > 1000) {
+ if (warn) {
+ mp_snprintf(s,128,
+ "invalid entry for `%s': too big value of SlantFont (%d/1000.0)",
+ fm->tfm_name, (int)fm->slant);
+ mp_warn(mp,s);
+ }
+ a += 16;
+ }
+ if (abs (fm->extend) > 2000) {
+ if (warn) {
+ mp_snprintf(s,128,
+ "invalid entry for `%s': too big value of ExtendFont (%d/1000.0)",
+ fm->tfm_name, (int)fm->extend);
+ mp_warn(mp,s);
+ }
+ a += 32;
+ }
+ if (fm->pid != -1 &&
+ !(is_truetype (fm) && is_included (fm) &&
+ is_subsetted (fm) && !is_reencoded (fm))) {
+ if (warn) {
+ mp_snprintf(s,128,
+ "invalid entry for `%s': "
+ "PidEid can be used only with subsetted non-reencoded TrueType fonts",
+ fm->tfm_name);
+ mp_warn(mp,s);
+ }
+ a += 64;
+ }
+ return a;
+}
+
+@ returns true if s is one of the 14 std. font names; speed-trimmed.
+
+@c static boolean check_basefont (char *s) {
+ static const char *basefont_names[] = {
+ "Courier", /* 0:7 */
+ "Courier-Bold", /* 1:12 */
+ "Courier-Oblique", /* 2:15 */
+ "Courier-BoldOblique", /* 3:19 */
+ "Helvetica", /* 4:9 */
+ "Helvetica-Bold", /* 5:14 */
+ "Helvetica-Oblique", /* 6:17 */
+ "Helvetica-BoldOblique", /* 7:21 */
+ "Symbol", /* 8:6 */
+ "Times-Roman", /* 9:11 */
+ "Times-Bold", /* 10:10 */
+ "Times-Italic", /* 11:12 */
+ "Times-BoldItalic", /* 12:16 */
+ "ZapfDingbats" /* 13:12 */
+ };
+ static const int Index[] =
+ { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6,
+ -1, 3, -1, 7
+ };
+ const size_t n = strlen (s);
+ int k = -1;
+ if (n > 21)
+ return false;
+ if (n == 12) { /* three names have length 12 */
+ switch (*s) {
+ case 'C':
+ k = 1; /* Courier-Bold */
+ break;
+ case 'T':
+ k = 11; /* Times-Italic */
+ break;
+ case 'Z':
+ k = 13; /* ZapfDingbats */
+ break;
+ default:
+ return false;
+ }
+ } else
+ k = Index[n];
+ if (k > -1 && !strcmp (basefont_names[k], s))
+ return true;
+ return false;
+}
+
+@
+@d is_cfg_comment(c) (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
+
+@c static void fm_scan_line (MP mp) {
+ int a, b, c, j, u = 0, v = 0;
+ float d;
+ fm_entry *fm;
+ char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
+ char *p, *q, *r, *s;
+ char warn_s[128];
+ switch (mp->ps->mitem->type) {
+ case MAPFILE:
+ p = fm_line;
+ do {
+ c = fm_getchar (mp);
+ append_char_to_buf (c, p, fm_line, FM_BUF_SIZE);
+ } while (c != 10);
+ *(--p) = '\0';
+ r = fm_line;
+ break;
+ case MAPLINE:
+ r = mp->ps->mitem->map_line;
+ break;
+ default:
+ assert (0);
+ }
+ if (*r == '\0' || is_cfg_comment (*r))
+ return;
+ fm = new_fm_entry (mp);
+ read_field (r, q, buf);
+ set_field (tfm_name);
+ p = r;
+ read_field (r, q, buf);
+ if (*buf != '<' && *buf != '"')
+ set_field (ps_name);
+ else
+ r = p; /* unget the field */
+ if (mp_isdigit (*r)) { /* font flags given */
+ fm->flags = atoi (r);
+ while (mp_isdigit (*r))
+ r++;
+ }
+ while (1) { /* loop through "specials", encoding, font file */
+ skip (r, ' ');
+ switch (*r) {
+ case '\0':
+ goto DONE;
+ case '"': /* opening quote */
+ r++;
+ u = v = 0;
+ do {
+ skip (r, ' ');
+ if (sscanf (r, "%f %n", &d, &j) > 0) {
+ s = r + j; /* jump behind number, eat also blanks, if any */
+ if (*(s - 1) == 'E' || *(s - 1) == 'e')
+ s--; /* e. g. 0.5ExtendFont: \%f = 0.5E */
+ if (str_prefix (s, "SlantFont")) {
+ d *= 1000.0; /* correct rounding also for neg. numbers */
+ fm->slant = (short int) (d > 0 ? d + 0.5 : d - 0.5);
+ r = s + strlen ("SlantFont");
+ } else if (str_prefix (s, "ExtendFont")) {
+ d *= 1000.0;
+ fm->extend = (short int) (d > 0 ? d + 0.5 : d - 0.5);
+ if (fm->extend == 1000)
+ fm->extend = 0;
+ r = s + strlen ("ExtendFont");
+ } else { /* unknown name */
+ for (r = s;
+ *r != ' ' && *r != '"' && *r != '\0';
+ r++); /* jump over name */
+ c = *r; /* remember char for temporary end of string */
+ *r = '\0';
+ mp_snprintf(warn_s,128,
+ "invalid entry for `%s': unknown name `%s' ignored",
+ fm->tfm_name, s);
+ mp_warn(mp,warn_s);
+ *r = c;
+ }
+ } else
+ for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
+ }
+ while (*r == ' ');
+ if (*r == '"') /* closing quote */
+ r++;
+ else {
+ mp_snprintf(warn_s,128,
+ "invalid entry for `%s': closing quote missing",
+ fm->tfm_name);
+ mp_warn(mp,warn_s);
+ goto bad_line;
+ }
+ break;
+ case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
+ if (sscanf (r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
+ fm->pid = (short int)a;
+ fm->eid = (short int)b;
+ r += c;
+ break;
+ } /* fallthrough */
+ default: /* encoding or font file specification */
+ a = b = 0;
+ if (*r == '<') {
+ a = *r++;
+ if (*r == '<' || *r == '[')
+ b = *r++;
+ }
+ read_field (r, q, buf);
+ /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
+ if (strlen (buf) > 4 && mp_strcasecmp (strend (buf) - 4, ".enc") == 0) {
+ fm->encoding = mp_add_enc (mp, buf);
+ u = v = 0; /* u, v used if intervening blank: "<< foo" */
+ } else if (strlen (buf) > 0) { /* file name given */
+ /* font file, formats:
+ * subsetting: '<cmr10.pfa'
+ * no subsetting: '<<cmr10.pfa'
+ * no embedding: 'cmr10.pfa'
+ */
+ if (a == '<' || u == '<') {
+ set_included (fm);
+ if ((a == '<' && b == 0) || (a == 0 && v == 0))
+ set_subsetted (fm);
+ /* otherwise b == '<' (or '[') => no subsetting */
+ }
+ set_field (ff_name);
+ u = v = 0;
+ } else {
+ u = a;
+ v = b;
+ }
+ }
+ }
+ DONE:
+ if (fm->ps_name != NULL && check_basefont (fm->ps_name))
+ set_basefont (fm);
+ if (is_fontfile (fm)
+ && mp_strcasecmp (strend (fm_fontfile (fm)) - 4, ".ttf") == 0)
+ set_truetype (fm);
+ if (check_fm_entry (mp,fm, true) != 0)
+ goto bad_line;
+ /*
+ Until here the map line has been completely scanned without errors;
+ fm points to a valid, freshly filled-out |fm_entry| structure.
+ Now follows the actual work of registering/deleting.
+ */
+ if (avl_do_entry (mp, fm, mp->ps->mitem->mode) == 0) /* if success */
+ return;
+ bad_line:
+ delete_fm_entry (fm);
+}
+
+@
+@c static void fm_read_info (MP mp) {
+ char *n;
+ char s[256];
+ if (mp->ps->tfm_tree == NULL)
+ create_avl_trees (mp);
+ if (mp->ps->mitem->map_line == NULL) /* nothing to do */
+ return;
+ mp->ps->mitem->lineno = 1;
+ switch (mp->ps->mitem->type) {
+ case MAPFILE:
+ n = mp->ps->mitem->map_line;
+ mp->ps->fm_file = (mp->open_file)(mp, n, "r", mp_filetype_fontmap);
+ if (!mp->ps->fm_file) {
+ mp_snprintf(s,256,"cannot open font map file %s",n);
+ mp_warn(mp,s);
+ } else {
+ unsigned save_selector = mp->selector;
+ mp_normalize_selector(mp);
+ mp_print (mp, "{");
+ mp_print (mp, n);
+ while (!fm_eof ()) {
+ fm_scan_line (mp);
+ mp->ps->mitem->lineno++;
+ }
+ fm_close ();
+ mp_print (mp,"}");
+ mp->selector = save_selector;
+ mp->ps->fm_file = NULL;
+ }
+ mp_xfree(n);
+ break;
+ case MAPLINE:
+ fm_scan_line (mp);
+ break;
+ default:
+ assert (0);
+ }
+ mp->ps->mitem->map_line = NULL; /* done with this line */
+ return;
+}
+
+@ @c static void init_fm (fm_entry * fm, font_number f) {
+ if (fm->tfm_num == null_font ) {
+ fm->tfm_num = f;
+ fm->tfm_avail = TFM_FOUND;
+ }
+}
+
+@ @<Exported function ...@>=
+fm_entry * mp_fm_lookup (MP mp, font_number f);
+
+@ @c
+fm_entry * mp_fm_lookup (MP mp, font_number f) {
+ char *tfm;
+ fm_entry *fm;
+ fm_entry tmp;
+ int e;
+ if (mp->ps->tfm_tree == NULL)
+ fm_read_info (mp); /* only to read default map file */
+ tfm = mp->font_name[f];
+ assert (strcmp (tfm, nontfm));
+ /* Look up for full <tfmname>[+-]<expand> */
+ tmp.tfm_name = tfm;
+ fm = (fm_entry *) avl_find (&tmp, mp->ps->tfm_tree);
+ if (fm != NULL) {
+ init_fm (fm, f);
+ return (fm_entry *) fm;
+ }
+ tfm = mk_base_tfm (mp, mp->font_name[f], &e);
+ if (tfm == NULL) /* not an expanded font, nothing to do */
+ return NULL;
+
+ tmp.tfm_name = tfm;
+ fm = (fm_entry *) avl_find (&tmp, mp->ps->tfm_tree);
+ if (fm != NULL) { /* found an entry with the base tfm name, e.g. cmr10 */
+ return (fm_entry *) fm; /* font expansion uses the base font */
+ }
+ return NULL;
+}
+
+@ Early check whether a font file exists. Used e. g. for replacing fonts
+ of embedded PDF files: Without font file, the font within the embedded
+ PDF-file is used. Search tree |ff_tree| is used in 1st instance, as it
+ may be faster than the |kpse_find_file()|, and |kpse_find_file()| is called
+ only once per font file name + expansion parameter. This might help
+ keeping speed, if many PDF pages with same fonts are to be embedded.
+
+ The |ff_tree| contains only font files, which are actually needed,
+ so this tree typically is much smaller than the |tfm_tree| or |ps_tree|.
+
+@c
+static ff_entry *check_ff_exist (MP mp, fm_entry * fm) {
+ ff_entry *ff;
+ ff_entry tmp;
+ void **aa;
+
+ assert (fm->ff_name != NULL);
+ tmp.ff_name = fm->ff_name;
+ ff = (ff_entry *) avl_find (&tmp, mp->ps->ff_tree);
+ if (ff == NULL) { /* not yet in database */
+ ff = new_ff_entry (mp);
+ ff->ff_name = mp_xstrdup (mp,fm->ff_name);
+ ff->ff_path = mp_xstrdup (mp,fm->ff_name);
+ aa = avl_probe (mp->ps->ff_tree, ff);
+ assert (aa != NULL);
+ }
+ return ff;
+}
+
+@ Process map file given by its name or map line contents. Items not
+beginning with [+-=] flush default map file, if it has not yet been
+read. Leading blanks and blanks immediately following [+-=] are ignored.
+
+
+@c static void mp_process_map_item (MP mp, char *s, int type) {
+ char *p;
+ int mode;
+ if (*s == ' ')
+ s++; /* ignore leading blank */
+ switch (*s) {
+ case '+': /* +mapfile.map, +mapline */
+ mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
+ s++;
+ break;
+ case '=': /* =mapfile.map, =mapline */
+ mode = FM_REPLACE; /* try to replace earlier entry */
+ s++;
+ break;
+ case '-': /* -mapfile.map, -mapline */
+ mode = FM_DELETE; /* try to delete entry */
+ s++;
+ break;
+ default:
+ mode = FM_DUPIGNORE; /* like +, but also: */
+ mp_xfree(mp->ps->mitem->map_line);
+ mp->ps->mitem->map_line = NULL; /* flush default map file name */
+ }
+ if (*s == ' ')
+ s++; /* ignore blank after [+-=] */
+ p = s; /* map item starts here */
+ switch (type) {
+ case MAPFILE: /* remove blank at end */
+ while (*p != '\0' && *p != ' ')
+ p++;
+ *p = '\0';
+ break;
+ case MAPLINE: /* blank at end allowed */
+ break;
+ default:
+ assert (0);
+ }
+ if (mp->ps->mitem->map_line != NULL) /* read default map file first */
+ fm_read_info (mp);
+ if (*s != '\0') { /* only if real item to process */
+ mp->ps->mitem->mode = mode;
+ mp->ps->mitem->type = type;
+ mp->ps->mitem->map_line = s;
+ fm_read_info (mp);
+ }
+}
+
+@ @<Exported function headers@>=
+void mp_map_file (MP mp, str_number t);
+void mp_map_line (MP mp, str_number t);
+void mp_init_map_file (MP mp, int is_troff);
+
+@ @c
+void mp_map_file (MP mp, str_number t) {
+ char *s = mp_xstrdup(mp,mp_str (mp,t));
+ mp_process_map_item (mp, s, MAPFILE);
+}
+void mp_map_line (MP mp, str_number t) {
+ char *s = mp_xstrdup(mp,mp_str (mp,t));
+ mp_process_map_item (mp, s, MAPLINE);
+}
+
+@
+@c void mp_init_map_file (MP mp, int is_troff) {
+ char *r;
+ mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
+ mp->ps->mitem->mode = FM_DUPIGNORE;
+ mp->ps->mitem->type = MAPFILE;
+ mp->ps->mitem->map_line = NULL;
+ r = (mp->find_file)(mp,"mpost.map", "r", mp_filetype_fontmap);
+ if (r != NULL) {
+ mp_xfree(r);
+ mp->ps->mitem->map_line = mp_xstrdup (mp,"mpost.map");
+ } else {
+ if (is_troff) {
+ mp->ps->mitem->map_line = mp_xstrdup (mp,"troff.map");
+ } else {
+ mp->ps->mitem->map_line = mp_xstrdup (mp,"pdftex.map");
+ }
+ }
+}
+
+@ @<Dealloc variables@>=
+if (mp->ps->mitem!=NULL) {
+ mp_xfree(mp->ps->mitem->map_line);
+ mp_xfree(mp->ps->mitem);
+}
+
+@ cleaning up...
+
+@c
+static void destroy_fm_entry_tfm (void *pa, void *pb) {
+ fm_entry *fm;
+ (void)pb;
+ fm = (fm_entry *) pa;
+ if (!has_pslink (fm))
+ delete_fm_entry (fm);
+ else
+ unset_tfmlink (fm);
+}
+static void destroy_fm_entry_ps (void *pa, void *pb) {
+ fm_entry *fm;
+ (void)pb;
+ fm = (fm_entry *) pa;
+ if (!has_tfmlink (fm))
+ delete_fm_entry (fm);
+ else
+ unset_pslink (fm);
+}
+static void destroy_ff_entry (void *pa, void *pb) {
+ ff_entry *ff;
+ (void)pb;
+ ff = (ff_entry *) pa;
+ delete_ff_entry (ff);
+}
+
+@ @<Declarations@>=
+static void fm_free (MP mp);
+
+@ @c
+static void fm_free (MP mp) {
+ if (mp->ps->tfm_tree != NULL)
+ avl_destroy (mp->ps->tfm_tree);
+ if (mp->ps->ps_tree != NULL)
+ avl_destroy (mp->ps->ps_tree);
+ if (mp->ps->ff_tree != NULL)
+ avl_destroy (mp->ps->ff_tree);
+}
+
+@ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
+PostScript names for fonts that do not have to be downloaded, i.e., fonts that
+can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
+one or more spaces, a PostScript name, and possibly a space and some other junk.
+This routine reads the table, updates |font_ps_name| entries starting after
+|last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|.
+
+@d ps_tab_name "psfonts.map" /* locates font name translation table */
+
+@<Exported function ...@>=
+void mp_read_psname_table (MP mp) ;
+
+@ @c
+void mp_read_psname_table (MP mp) {
+ font_number k;
+ if (mp->ps->mitem == NULL) {
+ mp->ps->mitem = mp_xmalloc (mp,1,sizeof(mapitem));
+ mp->ps->mitem->mode = FM_DUPIGNORE;
+ mp->ps->mitem->type = MAPFILE;
+ mp->ps->mitem->map_line = NULL;
+ }
+ mp->ps->mitem->map_line = mp_xstrdup (mp,ps_tab_name);
+ fm_read_info (mp);
+ for (k=mp->last_ps_fnum+1;k<=mp->last_fnum;k++) {
+ if (mp_has_fm_entry(mp, k, NULL)) {
+ mp->font_ps_name[k] = mp_fm_font_name(mp,k);
+ }
+ }
+ mp->last_ps_fnum=mp->last_fnum;
+}
+
+
+@ The traditional function is a lot shorter now.
+
+
+
+@* \[44c] Helper functions for Type1 fonts.
+
+@<Types...@>=
+typedef char char_entry;
+typedef unsigned char Byte;
+typedef Byte Bytef;
+
+@ @<Glob...@>=
+char_entry *char_ptr, *char_array;
+size_t char_limit;
+char *job_id_string;
+
+@ @<Set initial...@>=
+mp->ps->char_array = NULL;
+mp->ps->job_id_string = NULL;
+
+@
+@d SMALL_ARRAY_SIZE 256
+@d Z_NULL 0
+
+@c
+void mp_set_job_id (MP mp) {
+ char *name_string, *format_string, *s;
+ size_t slen;
+ if (mp->ps->job_id_string != NULL)
+ return;
+ if ( mp->job_name==NULL )
+ mp->job_name = mp_xstrdup(mp,"mpout");
+ name_string = mp_xstrdup (mp,mp->job_name);
+ format_string = mp_xstrdup (mp,mp->mem_ident);
+ slen = SMALL_BUF_SIZE +
+ strlen (name_string) +
+ strlen (format_string);
+ s = mp_xmalloc (mp,slen, sizeof (char));
+ @= /*@@-bufferoverflowhigh@@*/ @>
+ sprintf (s,"%.4u/%.2u/%.2u %.2u:%.2u %s %s",
+ ((unsigned)mp->internal[mp_year]>>16),
+ ((unsigned)mp->internal[mp_month]>>16),
+ ((unsigned)mp->internal[mp_day]>>16),
+ ((unsigned)mp->internal[mp_time]>>16) / 60,
+ ((unsigned)mp->internal[mp_time]>>16) % 60,
+ name_string, format_string);
+ @= /*@@=bufferoverflowhigh@@*/ @>
+ mp->ps->job_id_string = mp_xstrdup (mp,s);
+ mp_xfree (s);
+ mp_xfree (name_string);
+ mp_xfree (format_string);
+}
+static void fnstr_append (MP mp, const char *s) {
+ size_t l = strlen (s) + 1;
+ alloc_array (char, l, SMALL_ARRAY_SIZE);
+ strcat (mp->ps->char_ptr, s);
+ mp->ps->char_ptr = strend (mp->ps->char_ptr);
+}
+
+@ @<Exported function headers@>=
+void mp_set_job_id (MP mp) ;
+
+@ @<Dealloc variables@>=
+mp_xfree(mp->ps->job_id_string);
+
+@ this is not really a true crc32, but it should be just enough to keep
+ subsets prefixes somewhat disjunct
+
+@c
+static unsigned long crc32 (unsigned long oldcrc, const Byte *buf, size_t len) {
+ unsigned long ret = 0;
+ size_t i;
+ if (oldcrc==0)
+ ret = (unsigned long)((23<<24)+(45<<16)+(67<<8)+89);
+ else
+ for (i=0;i<len;i++)
+ ret = (ret<<2)+buf[i];
+ return ret;
+}
+static boolean mp_char_marked (MP mp,font_number f, eight_bits c) {
+ integer b; /* |char_base[f]| */
+ b=mp->char_base[f];
+ if ( (c>=mp->font_bc[f])&&(c<=mp->font_ec[f])&&(mp->font_info[b+c].qqqq.b3!=0) )
+ return true;
+ else
+ return false;
+}
+
+static void make_subset_tag (MP mp, fm_entry * fm_cur, char **glyph_names, font_number tex_font)
+{
+ char tag[7];
+ unsigned long crc;
+ int i;
+ size_t l ;
+ if (mp->ps->job_id_string ==NULL)
+ mp_fatal_error(mp, "no job id!");
+ l = strlen (mp->ps->job_id_string) + 1;
+
+ alloc_array (char, l, SMALL_ARRAY_SIZE);
+ strcpy (mp->ps->char_array, mp->ps->job_id_string);
+ mp->ps->char_ptr = strend (mp->ps->char_array);
+ if (fm_cur->tfm_name != NULL) {
+ fnstr_append (mp," TFM name: ");
+ fnstr_append (mp,fm_cur->tfm_name);
+ }
+ fnstr_append (mp," PS name: ");
+ if (fm_cur->ps_name != NULL)
+ fnstr_append (mp,fm_cur->ps_name);
+ fnstr_append (mp," Encoding: ");
+ if (fm_cur->encoding != NULL && (fm_cur->encoding)->file_name != NULL)
+ fnstr_append (mp,(fm_cur->encoding)->file_name);
+ else
+ fnstr_append (mp,"built-in");
+ fnstr_append (mp," CharSet: ");
+ for (i = 0; i < 256; i++)
+ if (mp_char_marked (mp,tex_font, (eight_bits)i) && glyph_names[i] != notdef) {
+ if (glyph_names[i]!=NULL) {
+ fnstr_append (mp,"/");
+ fnstr_append (mp,glyph_names[i]);
+ }
+ }
+ if (fm_cur->charset != NULL) {
+ fnstr_append (mp," Extra CharSet: ");
+ fnstr_append (mp, fm_cur->charset);
+ }
+ crc = crc32 (0L, Z_NULL, 0);
+ crc = crc32 (crc, (Bytef *) mp->ps->char_array, strlen (mp->ps->char_array));
+ /* we need to fit a 32-bit number into a string of 6 uppercase chars long;
+ * there are 26 uppercase chars ==> each char represents a number in range
+ * |0..25|. The maximal number that can be represented by the tag is
+ * $26^6 - 1$, which is a number between $2^28$ and $2^29$. Thus the bits |29..31|
+ * of the CRC must be dropped out.
+ */
+ for (i = 0; i < 6; i++) {
+ tag[i] = 'A' + crc % 26;
+ crc /= 26;
+ }
+ tag[6] = 0;
+ fm_cur->subset_tag = mp_xstrdup (mp,tag);
+}
+
+
+
+@
+@d external_enc() (fm_cur->encoding)->glyph_names
+@d is_used_char(c) mp_char_marked (mp, tex_font, (eight_bits)c)
+@d end_last_eexec_line()
+ mp->ps->hexline_length = HEXLINE_WIDTH;
+ end_hexline(mp);
+ mp->ps->t1_eexec_encrypt = false
+@d t1_log(s) mp_print(mp,(char *)s)
+@d t1_putchar(c) wps_chr(c)
+@d embed_all_glyphs(tex_font) false
+@d t1_char(c) c
+@d extra_charset() mp->ps->dvips_extra_charset
+@d update_subset_tag()
+@d fixedcontent true
+
+@<Glob...@>=
+#define PRINTF_BUF_SIZE 1024
+char *dvips_extra_charset;
+char *cur_enc_name;
+unsigned char *grid;
+char *ext_glyph_names[256];
+char print_buf[PRINTF_BUF_SIZE];
+int t1_byte_waiting;
+
+@ @<Set initial ...@>=
+mp->ps->dvips_extra_charset=NULL;
+mp->ps->t1_byte_waiting=0;
+
+@
+@d t1_ungetchar(A) mp->ps->t1_byte_waiting=(int)(A)
+@d t1_eof() (mp->eof_file)(mp,mp->ps->t1_file)
+@d t1_close() (mp->close_file)(mp,mp->ps->t1_file)
+@d valid_code(c) (c >= 0 && c < 256)
+
+@c
+static int t1_getchar (MP mp) {
+ size_t len = 1;
+ unsigned char abyte=0;
+ void *byte_ptr = &abyte;
+ if (mp->ps->t1_byte_waiting) {
+ abyte = mp->ps->t1_byte_waiting;
+ mp->ps->t1_byte_waiting = 0;
+ } else {
+ (mp->read_binary_file)(mp,mp->ps->t1_file,&byte_ptr,&len);
+ }
+ return (int)abyte;
+}
+
+@ @<Static variables in the outer block@>=
+static const char *standard_glyph_names[256] =
+ { notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ "space", "exclam", "quotedbl", "numbersign",
+ "dollar", "percent", "ampersand", "quoteright", "parenleft",
+ "parenright", "asterisk", "plus", "comma", "hyphen", "period",
+ "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
+ "eight", "nine", "colon", "semicolon", "less",
+ "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
+ "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
+ "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
+ "backslash", "bracketright", "asciicircum", "underscore",
+ "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
+ "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
+ "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, "exclamdown", "cent",
+ "sterling", "fraction", "yen", "florin", "section", "currency",
+ "quotesingle", "quotedblleft", "guillemotleft",
+ "guilsinglleft", "guilsinglright", "fi", "fl", notdef, "endash",
+ "dagger", "daggerdbl", "periodcentered", notdef,
+ "paragraph", "bullet", "quotesinglbase", "quotedblbase",
+ "quotedblright", "guillemotright", "ellipsis", "perthousand",
+ notdef, "questiondown", notdef, "grave", "acute", "circumflex",
+ "tilde", "macron", "breve", "dotaccent", "dieresis", notdef,
+ "ring", "cedilla", notdef, "hungarumlaut", "ogonek", "caron", "emdash",
+ notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, "AE", notdef, "ordfeminine", notdef, notdef,
+ notdef, notdef, "Lslash", "Oslash", "OE", "ordmasculine", notdef,
+ notdef, notdef, notdef, notdef, "ae", notdef, notdef,
+ notdef, "dotlessi", notdef, notdef, "lslash", "oslash", "oe",
+ "germandbls", notdef, notdef, notdef, notdef };
+static const char charstringname[] = "/CharStrings";
+
+@ @<Glob...@>=
+char **t1_glyph_names;
+char *t1_builtin_glyph_names[256];
+char charsetstr[0x4000];
+boolean read_encoding_only;
+int t1_encoding;
+
+@ @c
+#define T1_BUF_SIZE 0x10
+
+#define CS_HSTEM 1
+#define CS_VSTEM 3
+#define CS_VMOVETO 4
+#define CS_RLINETO 5
+#define CS_HLINETO 6
+#define CS_VLINETO 7
+#define CS_RRCURVETO 8
+#define CS_CLOSEPATH 9
+#define CS_CALLSUBR 10
+#define CS_RETURN 11
+#define CS_ESCAPE 12
+#define CS_HSBW 13
+#define CS_ENDCHAR 14
+#define CS_RMOVETO 21
+#define CS_HMOVETO 22
+#define CS_VHCURVETO 30
+#define CS_HVCURVETO 31
+#define CS_1BYTE_MAX (CS_HVCURVETO + 1)
+
+#define CS_DOTSECTION CS_1BYTE_MAX + 0
+#define CS_VSTEM3 CS_1BYTE_MAX + 1
+#define CS_HSTEM3 CS_1BYTE_MAX + 2
+#define CS_SEAC CS_1BYTE_MAX + 6
+#define CS_SBW CS_1BYTE_MAX + 7
+#define CS_DIV CS_1BYTE_MAX + 12
+#define CS_CALLOTHERSUBR CS_1BYTE_MAX + 16
+#define CS_POP CS_1BYTE_MAX + 17
+#define CS_SETCURRENTPOINT CS_1BYTE_MAX + 33
+#define CS_2BYTE_MAX (CS_SETCURRENTPOINT + 1)
+#define CS_MAX CS_2BYTE_MAX
+
+@ @<Types...@>=
+typedef unsigned char byte;
+typedef struct {
+ byte nargs; /* number of arguments */
+ boolean bottom; /* take arguments from bottom of stack? */
+ boolean clear; /* clear stack? */
+ boolean valid;
+} cc_entry; /* CharString Command */
+typedef struct {
+ char *glyph_name; /* glyph name (or notdef for Subrs entry) */
+ byte *data;
+ unsigned short len; /* length of the whole string */
+ unsigned short cslen; /* length of the encoded part of the string */
+ boolean is_used;
+ boolean valid;
+} cs_entry;
+
+@
+@d t1_c1 52845
+@d t1_c2 22719
+
+@<Glob...@>=
+unsigned short t1_dr, t1_er;
+unsigned short t1_cslen;
+short t1_lenIV;
+
+@ @<Types...@>=
+typedef char t1_line_entry;
+typedef char t1_buf_entry;
+
+@ @<Glob...@>=
+t1_line_entry *t1_line_ptr, *t1_line_array;
+size_t t1_line_limit;
+t1_buf_entry *t1_buf_ptr, *t1_buf_array;
+size_t t1_buf_limit;
+int cs_start;
+cs_entry *cs_tab, *cs_ptr, *cs_notdef;
+char *cs_dict_start, *cs_dict_end;
+int cs_count, cs_size, cs_size_pos;
+cs_entry *subr_tab;
+char *subr_array_start, *subr_array_end;
+int subr_max, subr_size, subr_size_pos;
+
+@ @<Set initial...@>=
+mp->ps->t1_line_array = NULL;
+mp->ps->t1_buf_array = NULL;
+
+@
+ This list contains the begin/end tokens commonly used in the
+ /Subrs array of a Type 1 font.
+
+@<Static variables in the outer block@>=
+static const char *cs_token_pairs_list[][2] = {
+ {" RD", "NP"},
+ {" -|", "|"},
+ {" RD", "noaccess put"},
+ {" -|", "noaccess put"},
+ {NULL, NULL}
+};
+
+@ @<Glob...@>=
+const char **cs_token_pair;
+boolean t1_pfa, t1_cs, t1_scan, t1_eexec_encrypt, t1_synthetic;
+int t1_in_eexec; /* 0 before eexec-encrypted, 1 during, 2 after */
+int t1_block_length;
+int last_hexbyte;
+void *t1_file;
+int hexline_length;
+
+@
+@d HEXLINE_WIDTH 64
+
+@<Set initial ...@>=
+mp->ps->hexline_length = 0;
+
+@
+@d t1_prefix(s) str_prefix(mp->ps->t1_line_array, s)
+@d t1_buf_prefix(s) str_prefix(mp->ps->t1_buf_array, s)
+@d t1_suffix(s) str_suffix(mp->ps->t1_line_array, mp->ps->t1_line_ptr, s)
+@d t1_buf_suffix(s) str_suffix(mp->ps->t1_buf_array, mp->ps->t1_buf_ptr, s)
+@d t1_charstrings() strstr(mp->ps->t1_line_array, charstringname)
+@d t1_subrs() t1_prefix("/Subrs")
+@d t1_end_eexec() t1_suffix("mark currentfile closefile")
+@d t1_cleartomark() t1_prefix("cleartomark")
+
+@c
+static void end_hexline (MP mp) {
+ if (mp->ps->hexline_length >= HEXLINE_WIDTH) {
+ wps_cr;
+ mp->ps->hexline_length = 0;
+ }
+}
+static void t1_check_pfa (MP mp) {
+ const int c = t1_getchar (mp);
+ mp->ps->t1_pfa = (c != 128) ? true : false;
+ t1_ungetchar (c);
+}
+static int t1_getbyte (MP mp)
+{
+ int c = t1_getchar (mp);
+ if (mp->ps->t1_pfa)
+ return c;
+ if (mp->ps->t1_block_length == 0) {
+ if (c != 128)
+ mp_fatal_error (mp, "invalid marker");
+ c = t1_getchar (mp);
+ if (c == 3) {
+ while (!t1_eof ())
+ (void)t1_getchar (mp);
+ return EOF;
+ }
+ mp->ps->t1_block_length = t1_getchar (mp) & 0xff;
+ mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 8;
+ mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 16;
+ mp->ps->t1_block_length |= (unsigned)(t1_getchar (mp) & 0xff) << 24;
+ c = t1_getchar (mp);
+ }
+ mp->ps->t1_block_length--;
+ return c;
+}
+static int hexval (int c) {
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else if (c >= '0' && c <= '9')
+ return c - '0';
+ else
+ return -1;
+}
+static byte edecrypt (MP mp, byte cipher) {
+ byte plain;
+ if (mp->ps->t1_pfa) {
+ while (cipher == 10 || cipher == 13)
+ cipher = (byte)t1_getbyte (mp);
+ mp->ps->last_hexbyte = cipher = (byte)(((byte)hexval (cipher) << 4) +
+ hexval (t1_getbyte (mp)));
+ }
+ plain = (cipher ^ (mp->ps->t1_dr >> 8));
+ mp->ps->t1_dr = (cipher + mp->ps->t1_dr) * t1_c1 + t1_c2;
+ return plain;
+}
+static byte cdecrypt (byte cipher, unsigned short *cr)
+{
+ const byte plain = (cipher ^ (*cr >> 8));
+ *cr = (cipher + *cr) * t1_c1 + t1_c2;
+ return plain;
+}
+static byte eencrypt (MP mp, byte plain)
+{
+ const byte cipher = (plain ^ (mp->ps->t1_er >> 8));
+ mp->ps->t1_er = (cipher + mp->ps->t1_er) * t1_c1 + t1_c2;
+ return cipher;
+}
+
+static byte cencrypt (byte plain, unsigned short *cr)
+{
+ const byte cipher = (plain ^ (*cr >> 8));
+ *cr = (cipher + *cr) * t1_c1 + t1_c2;
+ return cipher;
+}
+
+static char *eol (char *s) {
+ char *p = strend (s);
+ if (p!=NULL && p - s > 1 && p[-1] != 10) {
+ *p++ = 10;
+ *p = 0;
+ }
+ return p;
+}
+static float t1_scan_num (MP mp, char *p, char **r)
+{
+ float f;
+ char s[128];
+ skip (p, ' ');
+ if (sscanf (p, "%g", &f) != 1) {
+ remove_eol (p, mp->ps->t1_line_array);
+ mp_snprintf(s,128, "a number expected: `%s'", mp->ps->t1_line_array);
+ mp_fatal_error(mp,s);
+ }
+ if (r != NULL) {
+ for (; mp_isdigit (*p) || *p == '.' ||
+ *p == 'e' || *p == 'E' || *p == '+' || *p == '-'; p++);
+ *r = p;
+ }
+ return f;
+}
+
+static boolean str_suffix (const char *begin_buf, const char *end_buf,
+ const char *s)
+{
+ const char *s1 = end_buf - 1, *s2 = strend (s) - 1;
+ if (*s1 == 10)
+ s1--;
+ while (s1 >= begin_buf && s2 >= s) {
+ if (*s1-- != *s2--)
+ return false;
+ }
+ return s2 < s;
+}
+
+@
+
+@d alloc_array(T, n, s) do {
+ if (mp->ps->T##_array == NULL) {
+ mp->ps->T##_limit = (s);
+ if ((size_t)(n) > mp->ps->T##_limit)
+ mp->ps->T##_limit = (size_t)(n);
+ mp->ps->T##_array = mp_xmalloc (mp,mp->ps->T##_limit,sizeof(T##_entry));
+ mp->ps->T##_ptr = mp->ps->T##_array;
+ }
+ else if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit) {
+ size_t last_ptr_index;
+ last_ptr_index = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array);
+ mp->ps->T##_limit *= 2;
+ if ((size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n)) > mp->ps->T##_limit)
+ mp->ps->T##_limit = (size_t)(mp->ps->T##_ptr - mp->ps->T##_array + (n));
+ mp->ps->T##_array = mp_xrealloc(mp,mp->ps->T##_array,mp->ps->T##_limit, sizeof(T##_entry));
+ mp->ps->T##_ptr = mp->ps->T##_array + last_ptr_index;
+ }
+} while (0)
+
+@d out_eexec_char(A) t1_outhex(mp,(A))
+
+@c
+static void t1_outhex (MP mp, byte b)
+{
+ static const char *hexdigits = "0123456789ABCDEF";
+ t1_putchar (hexdigits[b / 16]);
+ t1_putchar (hexdigits[b % 16]);
+ mp->ps->hexline_length += 2;
+ end_hexline (mp);
+}
+static void t1_getline (MP mp) {
+ int c, l, eexec_scan;
+ char *p;
+ static const char eexec_str[] = "currentfile eexec";
+ static int eexec_len = 17; /* |strlen(eexec_str)| */
+ RESTART:
+ if (t1_eof ())
+ mp_fatal_error (mp,"unexpected end of file");
+ mp->ps->t1_line_ptr = mp->ps->t1_line_array;
+ alloc_array (t1_line, 1, T1_BUF_SIZE);
+ mp->ps->t1_cslen = 0;
+ eexec_scan = 0;
+ c = t1_getbyte (mp);
+ if (c == EOF)
+ goto EXIT;
+ while (!t1_eof ()) {
+ if (mp->ps->t1_in_eexec == 1)
+ c = edecrypt (mp,(byte)c);
+ alloc_array (t1_line, 1, T1_BUF_SIZE);
+ append_char_to_buf (c, mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
+ if (mp->ps->t1_in_eexec == 0 && eexec_scan >= 0 && eexec_scan < eexec_len) {
+ if (mp->ps->t1_line_array[eexec_scan] == eexec_str[eexec_scan])
+ eexec_scan++;
+ else
+ eexec_scan = -1;
+ }
+ if (c == 10 || (mp->ps->t1_pfa && eexec_scan == eexec_len && c == 32))
+ break;
+ if (mp->ps->t1_cs && mp->ps->t1_cslen == 0 &&
+ (mp->ps->t1_line_ptr - mp->ps->t1_line_array > 4) &&
+ (t1_suffix (" RD ") || t1_suffix (" -| "))) {
+ p = mp->ps->t1_line_ptr - 5;
+ while (*p != ' ')
+ p--;
+ l = (int)t1_scan_num (mp, p + 1, 0);
+ mp->ps->t1_cslen = (unsigned short)l;
+ mp->ps->cs_start = mp->ps->t1_line_ptr - mp->ps->t1_line_array;
+ /* |mp->ps->cs_start| is an index now */
+ alloc_array (t1_line, l, T1_BUF_SIZE);
+ while (l-- > 0) {
+ *mp->ps->t1_line_ptr = (t1_line_entry)edecrypt (mp,(byte)t1_getbyte (mp));
+ mp->ps->t1_line_ptr++;
+ }
+ }
+ c = t1_getbyte (mp);
+ }
+ alloc_array (t1_line, 2, T1_BUF_SIZE); /* |append_eol| can append 2 chars */
+ append_eol (mp->ps->t1_line_ptr, mp->ps->t1_line_array, mp->ps->t1_line_limit);
+ if (mp->ps->t1_line_ptr - mp->ps->t1_line_array < 2)
+ goto RESTART;
+ if (eexec_scan == eexec_len)
+ mp->ps->t1_in_eexec = 1;
+ EXIT:
+ /* ensure that |mp->ps->t1_buf_array| has as much room as |t1_line_array| */
+ mp->ps->t1_buf_ptr = mp->ps->t1_buf_array;
+ alloc_array (t1_buf, mp->ps->t1_line_limit, mp->ps->t1_line_limit);
+}
+
+static void t1_putline (MP mp)
+{
+ char *p = mp->ps->t1_line_array;
+ if (mp->ps->t1_line_ptr - mp->ps->t1_line_array <= 1)
+ return;
+ if (mp->ps->t1_eexec_encrypt) {
+ while (p < mp->ps->t1_line_ptr)
+ out_eexec_char (eencrypt (mp,(byte)*p++));
+ } else {
+ while (p < mp->ps->t1_line_ptr)
+ t1_putchar (*p++);
+ }
+}
+
+static void t1_puts (MP mp, const char *s)
+{
+ if (s != mp->ps->t1_line_array)
+ strcpy (mp->ps->t1_line_array, s);
+ mp->ps->t1_line_ptr = strend (mp->ps->t1_line_array);
+ t1_putline (mp);
+}
+
+static void t1_init_params (MP mp, const char *open_name_prefix,
+ char *cur_file_name) {
+ if ((open_name_prefix != NULL) && strlen(open_name_prefix)) {
+ t1_log (open_name_prefix);
+ t1_log (cur_file_name);
+ }
+ mp->ps->t1_lenIV = 4;
+ mp->ps->t1_dr = 55665;
+ mp->ps->t1_er = 55665;
+ mp->ps->t1_in_eexec = 0;
+ mp->ps->t1_cs = false;
+ mp->ps->t1_scan = true;
+ mp->ps->t1_synthetic = false;
+ mp->ps->t1_eexec_encrypt = false;
+ mp->ps->t1_block_length = 0;
+ t1_check_pfa (mp);
+}
+static void t1_close_font_file (MP mp, const char *close_name_suffix) {
+ if ((close_name_suffix != NULL) && strlen(close_name_suffix)) {
+ t1_log (close_name_suffix);
+ }
+ t1_close ();
+}
+
+static void t1_check_block_len (MP mp, boolean decrypt) {
+ int l, c;
+ char s[128];
+ if (mp->ps->t1_block_length == 0)
+ return;
+ c = t1_getbyte (mp);
+ if (decrypt)
+ c = edecrypt (mp,(byte)c);
+ l = mp->ps->t1_block_length;
+ if (!(l == 0 && (c == 10 || c == 13))) {
+ mp_snprintf(s,128,"%i bytes more than expected were ignored", l+ 1);
+ mp_warn(mp,s);
+ while (l-- > 0)
+ (void)t1_getbyte (mp);
+ }
+}
+static void t1_start_eexec (MP mp, fm_entry *fm_cur) {
+ int i;
+ if (!mp->ps->t1_pfa)
+ t1_check_block_len (mp, false);
+ for (mp->ps->t1_line_ptr = mp->ps->t1_line_array, i = 0; i < 4; i++) {
+ (void)edecrypt (mp, (byte)t1_getbyte (mp));
+ *mp->ps->t1_line_ptr++ = 0;
+ }
+ mp->ps->t1_eexec_encrypt = true;
+ if (!mp->ps->read_encoding_only)
+ if (is_included (fm_cur))
+ t1_putline (mp); /* to put the first four bytes */
+}
+static void t1_stop_eexec (MP mp) {
+ int c;
+ end_last_eexec_line ();
+ if (!mp->ps->t1_pfa)
+ t1_check_block_len (mp,true);
+ else {
+ c = edecrypt (mp, (byte)t1_getbyte (mp));
+ if (!(c == 10 || c == 13)) {
+ if (mp->ps->last_hexbyte == 0)
+ t1_puts (mp,"00");
+ else
+ mp_warn (mp,"unexpected data after eexec");
+ }
+ }
+ mp->ps->t1_cs = false;
+ mp->ps->t1_in_eexec = 2;
+}
+static void t1_modify_fm (MP mp) {
+ mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
+}
+
+static void t1_modify_italic (MP mp) {
+ mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
+}
+
+@ @<Types...@>=
+typedef struct {
+ const char *pdfname;
+ const char *t1name;
+ float value;
+ boolean valid;
+} key_entry;
+
+@
+@d FONT_KEYS_NUM 11
+
+@<Declarations@>=
+static key_entry font_keys[FONT_KEYS_NUM] = {
+ {"Ascent", "Ascender", 0, false},
+ {"CapHeight", "CapHeight", 0, false},
+ {"Descent", "Descender", 0, false},
+ {"FontName", "FontName", 0, false},
+ {"ItalicAngle", "ItalicAngle", 0, false},
+ {"StemV", "StdVW", 0, false},
+ {"XHeight", "XHeight", 0, false},
+ {"FontBBox", "FontBBox", 0, false},
+ {"", "", 0, false},
+ {"", "", 0, false},
+ {"", "", 0, false}
+};
+
+
+@
+@d ASCENT_CODE 0
+@d CAPHEIGHT_CODE 1
+@d DESCENT_CODE 2
+@d FONTNAME_CODE 3
+@d ITALIC_ANGLE_CODE 4
+@d STEMV_CODE 5
+@d XHEIGHT_CODE 6
+@d FONTBBOX1_CODE 7
+@d FONTBBOX2_CODE 8
+@d FONTBBOX3_CODE 9
+@d FONTBBOX4_CODE 10
+@d MAX_KEY_CODE (FONTBBOX1_CODE + 1)
+
+@c
+static void t1_scan_keys (MP mp, font_number tex_font,fm_entry *fm_cur) {
+ int i, k;
+ char *p, *r;
+ key_entry *key;
+ if (fm_extend (fm_cur) != 0 || fm_slant (fm_cur) != 0) {
+ if (t1_prefix ("/FontMatrix")) {
+ t1_modify_fm (mp);
+ return;
+ }
+ if (t1_prefix ("/ItalicAngle")) {
+ t1_modify_italic (mp);
+ return;
+ }
+ }
+ if (t1_prefix ("/FontType")) {
+ p = mp->ps->t1_line_array + strlen ("FontType") + 1;
+ if ((i = (int)t1_scan_num (mp,p, 0)) != 1) {
+ char s[128];
+ mp_snprintf(s,125,"Type%d fonts unsupported by metapost", i);
+ mp_fatal_error(mp,s);
+ }
+ return;
+ }
+ for (key = font_keys; key - font_keys < MAX_KEY_CODE; key++)
+ if (str_prefix (mp->ps->t1_line_array + 1, key->t1name))
+ break;
+ if (key - font_keys == MAX_KEY_CODE)
+ return;
+ key->valid = true;
+ p = mp->ps->t1_line_array + strlen (key->t1name) + 1;
+ skip (p, ' ');
+ if ((k = key - font_keys) == FONTNAME_CODE) {
+ if (*p != '/') {
+ char s[128];
+ remove_eol (p, mp->ps->t1_line_array);
+ mp_snprintf(s,128,"a name expected: `%s'", mp->ps->t1_line_array);
+ mp_fatal_error(mp,s);
+ }
+ r = ++p; /* skip the slash */
+ if (is_included (fm_cur)) {
+ /* save the fontname */
+ strncpy (mp->ps->fontname_buf, p, FONTNAME_BUF_SIZE);
+ for (i=0; mp->ps->fontname_buf[i] != 10; i++);
+ mp->ps->fontname_buf[i]=0;
+
+ if(is_subsetted (fm_cur)) {
+ if (fm_cur->encoding!=NULL && fm_cur->encoding->glyph_names!=NULL)
+ make_subset_tag (mp,fm_cur, fm_cur->encoding->glyph_names, tex_font);
+ else
+ make_subset_tag (mp,fm_cur, mp->ps->t1_builtin_glyph_names, tex_font);
+
+ alloc_array (t1_line, (r-mp->ps->t1_line_array+6+1+strlen(mp->ps->fontname_buf)+1),
+ T1_BUF_SIZE);
+ strncpy (r, fm_cur->subset_tag , 6);
+ *(r+6) = '-';
+ strncpy (r+7, mp->ps->fontname_buf, strlen(mp->ps->fontname_buf)+1);
+ mp->ps->t1_line_ptr = eol (r);
+ } else {
+ /* |for (q = p; *q != ' ' && *q != 10; *q++);|*/
+ /*|*q = 0;|*/
+ mp->ps->t1_line_ptr = eol (r);
+ }
+ }
+ return;
+ }
+ if ((k == STEMV_CODE || k == FONTBBOX1_CODE)
+ && (*p == '[' || *p == '{'))
+ p++;
+ if (k == FONTBBOX1_CODE) {
+ for (i = 0; i < 4; i++) {
+ key[i].value = t1_scan_num (mp, p, &r);
+ p = r;
+ }
+ return;
+ }
+ key->value = t1_scan_num (mp, p, 0);
+}
+static void t1_scan_param (MP mp, font_number tex_font,fm_entry *fm_cur)
+{
+ static const char *lenIV = "/lenIV";
+ if (!mp->ps->t1_scan || *mp->ps->t1_line_array != '/')
+ return;
+ if (t1_prefix (lenIV)) {
+ mp->ps->t1_lenIV = (short int)t1_scan_num (mp,mp->ps->t1_line_array + strlen (lenIV), 0);
+ return;
+ }
+ t1_scan_keys (mp, tex_font,fm_cur);
+}
+static void copy_glyph_names (MP mp, char **glyph_names, int a, int b) {
+ if (glyph_names[b] != notdef) {
+ mp_xfree (glyph_names[b]);
+ glyph_names[b] = (char *) notdef;
+ }
+ if (glyph_names[a] != notdef) {
+ glyph_names[b] = mp_xstrdup (mp,glyph_names[a]);
+ }
+}
+static void t1_builtin_enc (MP mp) {
+ int i, a, b, c, counter = 0;
+ char *r, *p;
+ /*
+ * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|
+ */
+ if (t1_suffix ("def")) { /* predefined encoding */
+ (void)sscanf (mp->ps->t1_line_array + strlen ("/Encoding"), "%256s", mp->ps->t1_buf_array);
+ if (strcmp (mp->ps->t1_buf_array, "StandardEncoding") == 0) {
+ for (i = 0; i < 256; i++)
+ if (standard_glyph_names[i] == notdef)
+ mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
+ else
+ mp->ps->t1_builtin_glyph_names[i] =
+ mp_xstrdup (mp,standard_glyph_names[i]);
+ mp->ps->t1_encoding = ENC_STANDARD;
+ } else {
+ char s[128];
+ mp_snprintf(s,128, "cannot subset font (unknown predefined encoding `%s')",
+ mp->ps->t1_buf_array);
+ mp_fatal_error(mp,s);
+ }
+ return;
+ } else
+ mp->ps->t1_encoding = ENC_BUILTIN;
+ /*
+ * At this moment "/Encoding" is the prefix of |mp->ps->t1_line_array|, and the encoding is
+ * not a predefined encoding
+ *
+ * We have two possible forms of Encoding vector. The first case is
+ *
+ * /Encoding [/a /b /c...] readonly def
+ *
+ * and the second case can look like
+ *
+ * /Encoding 256 array 0 1 255 {1 index exch /.notdef put} for
+ * dup 0 /x put
+ * dup 1 /y put
+ * ...
+ * readonly def
+ */
+ for (i = 0; i < 256; i++)
+ mp->ps->t1_builtin_glyph_names[i] = (char *) notdef;
+ if (t1_prefix ("/Encoding [") || t1_prefix ("/Encoding[")) { /* the first case */
+ r = strchr (mp->ps->t1_line_array, '[') + 1;
+ skip (r, ' ');
+ for (;;) {
+ while (*r == '/') {
+ for (p = mp->ps->t1_buf_array, r++;
+ *r != 32 && *r != 10 && *r != ']' && *r != '/';
+ *p++ = *r++);
+ *p = 0;
+ skip (r, ' ');
+ if (counter > 255) {
+ mp_fatal_error
+ (mp, "encoding vector contains more than 256 names");
+ }
+ if (strcmp (mp->ps->t1_buf_array, notdef) != 0)
+ mp->ps->t1_builtin_glyph_names[counter] = mp_xstrdup (mp,mp->ps->t1_buf_array);
+ counter++;
+ }
+ if (*r != 10 && *r != '%') {
+ if (str_prefix (r, "] def")
+ || str_prefix (r, "] readonly def"))
+ break;
+ else {
+ char s[128];
+ remove_eol (r, mp->ps->t1_line_array);
+ mp_snprintf(s,128,"a name or `] def' or `] readonly def' expected: `%s'",
+ mp->ps->t1_line_array);
+ mp_fatal_error(mp,s);
+ }
+ }
+ t1_getline (mp);
+ r = mp->ps->t1_line_array;
+ }
+ } else { /* the second case */
+ p = strchr (mp->ps->t1_line_array, 10);
+ for (;p!=NULL;) {
+ if (*p == 10) {
+ t1_getline (mp);
+ p = mp->ps->t1_line_array;
+ }
+ /*
+ check for `dup <index> <glyph> put'
+ */
+ if (sscanf (p, "dup %i%256s put", &i, mp->ps->t1_buf_array) == 2 &&
+ *mp->ps->t1_buf_array == '/' && valid_code (i)) {
+ if (strcmp (mp->ps->t1_buf_array + 1, notdef) != 0)
+ mp->ps->t1_builtin_glyph_names[i] =
+ mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
+ p = strstr (p, " put") + strlen (" put");
+ skip (p, ' ');
+ }
+ /*
+ check for `dup dup <to> exch <from> get put'
+ */
+ else if (sscanf (p, "dup dup %i exch %i get put", &b, &a) == 2
+ && valid_code (a) && valid_code (b)) {
+ copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a, b);
+ p = strstr (p, " get put") + strlen (" get put");
+ skip (p, ' ');
+ }
+ /*
+ check for `dup dup <from> <size> getinterval <to> exch putinterval'
+ */
+ else if (sscanf
+ (p, "dup dup %i %i getinterval %i exch putinterval",
+ &a, &c, &b) == 3 && valid_code (a) && valid_code (b)
+ && valid_code (c)) {
+ for (i = 0; i < c; i++)
+ copy_glyph_names (mp,mp->ps->t1_builtin_glyph_names, a + i, b + i);
+ p = strstr (p, " putinterval") + strlen (" putinterval");
+ skip (p, ' ');
+ }
+ /*
+ check for `def' or `readonly def'
+ */
+ else if ((p == mp->ps->t1_line_array || (p > mp->ps->t1_line_array && p[-1] == ' '))
+ && strcmp (p, "def\n") == 0)
+ return;
+ /*
+ skip an unrecognizable word
+ */
+ else {
+ while (*p != ' ' && *p != 10)
+ p++;
+ skip (p, ' ');
+ }
+ }
+ }
+}
+
+static void t1_check_end (MP mp) {
+ if (t1_eof ())
+ return;
+ t1_getline (mp);
+ if (t1_prefix ("{restore}"))
+ t1_putline (mp);
+}
+
+@ @<Types...@>=
+typedef struct {
+ char *ff_name; /* base name of font file */
+ char *ff_path; /* full path to font file */
+} ff_entry;
+
+@ @c
+static boolean t1_open_fontfile (MP mp, fm_entry *fm_cur,const char *open_name_prefix) {
+ ff_entry *ff;
+ ff = check_ff_exist (mp, fm_cur);
+ mp->ps->t1_file = NULL;
+ if (ff->ff_path != NULL) {
+ mp->ps->t1_file = (mp->open_file)(mp,ff->ff_path, "r", mp_filetype_font);
+ }
+ if (mp->ps->t1_file == NULL) {
+ mp_warn (mp, "cannot open Type 1 font file for reading");
+ return false;
+ }
+ t1_init_params (mp,open_name_prefix,fm_cur->ff_name);
+ mp->ps->fontfile_found = true;
+ return true;
+}
+
+static void t1_scan_only (MP mp, font_number tex_font, fm_entry *fm_cur) {
+ do {
+ t1_getline (mp);
+ t1_scan_param (mp,tex_font, fm_cur);
+ }
+ while (mp->ps->t1_in_eexec == 0);
+ t1_start_eexec (mp,fm_cur);
+ do {
+ t1_getline (mp);
+ t1_scan_param (mp,tex_font, fm_cur);
+ }
+ while (!(t1_charstrings () || t1_subrs ()));
+}
+
+static void t1_include (MP mp, font_number tex_font, fm_entry *fm_cur) {
+ do {
+ t1_getline (mp);
+ t1_scan_param (mp,tex_font, fm_cur);
+ t1_putline (mp);
+ }
+ while (mp->ps->t1_in_eexec == 0);
+ t1_start_eexec (mp,fm_cur);
+ do {
+ t1_getline (mp);
+ t1_scan_param (mp,tex_font, fm_cur);
+ t1_putline (mp);
+ }
+ while (!(t1_charstrings () || t1_subrs ()));
+ mp->ps->t1_cs = true;
+ do {
+ t1_getline (mp);
+ t1_putline (mp);
+ }
+ while (!t1_end_eexec ());
+ t1_stop_eexec (mp);
+ if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
+ do {
+ t1_getline (mp);
+ t1_putline (mp);
+ }
+ while (!t1_cleartomark ());
+ t1_check_end (mp); /* write "{restore}if" if found */
+ }
+}
+
+@
+@d check_subr(SUBR) if (SUBR >= mp->ps->subr_size || SUBR < 0) {
+ char s[128];
+ mp_snprintf(s,128,"Subrs array: entry index out of range (%i)",SUBR);
+ mp_fatal_error(mp,s);
+ }
+
+@c
+static const char **check_cs_token_pair (MP mp) {
+ const char **p = (const char **) cs_token_pairs_list;
+ for (; p[0] != NULL; ++p)
+ if (t1_buf_prefix (p[0]) && t1_buf_suffix (p[1]))
+ return p;
+ return NULL;
+}
+
+static void cs_store (MP mp, boolean is_subr) {
+ char *p;
+ cs_entry *ptr;
+ int subr;
+ for (p = mp->ps->t1_line_array, mp->ps->t1_buf_ptr = mp->ps->t1_buf_array; *p != ' ';
+ *mp->ps->t1_buf_ptr++ = *p++);
+ *mp->ps->t1_buf_ptr = 0;
+ if (is_subr) {
+ subr = (int)t1_scan_num (mp, p + 1, 0);
+ check_subr (subr);
+ ptr = mp->ps->subr_tab + subr;
+ } else {
+ ptr = mp->ps->cs_ptr++;
+ if (mp->ps->cs_ptr - mp->ps->cs_tab > mp->ps->cs_size) {
+ char s[128];
+ mp_snprintf(s,128,"CharStrings dict: more entries than dict size (%i)",mp->ps->cs_size);
+ mp_fatal_error(mp,s);
+ }
+ if (strcmp (mp->ps->t1_buf_array + 1, notdef) == 0) /* skip the slash */
+ ptr->glyph_name = (char *) notdef;
+ else
+ ptr->glyph_name = mp_xstrdup (mp,mp->ps->t1_buf_array + 1);
+ }
+ /* copy " RD " + cs data to |mp->ps->t1_buf_array| */
+ memcpy (mp->ps->t1_buf_array, mp->ps->t1_line_array + mp->ps->cs_start - 4,
+ (size_t) (mp->ps->t1_cslen + 4));
+ /* copy the end of cs data to |mp->ps->t1_buf_array| */
+ for (p = mp->ps->t1_line_array + mp->ps->cs_start + mp->ps->t1_cslen, mp->ps->t1_buf_ptr =
+ mp->ps->t1_buf_array + mp->ps->t1_cslen + 4; *p != 10; *mp->ps->t1_buf_ptr++ = *p++);
+ *mp->ps->t1_buf_ptr++ = 10;
+ if (is_subr && mp->ps->cs_token_pair == NULL)
+ mp->ps->cs_token_pair = check_cs_token_pair (mp);
+ ptr->len = (unsigned short)(mp->ps->t1_buf_ptr - mp->ps->t1_buf_array);
+ ptr->cslen = mp->ps->t1_cslen;
+ ptr->data = mp_xmalloc (mp, (size_t)ptr->len , sizeof (byte));
+ memcpy (ptr->data, mp->ps->t1_buf_array, (size_t)ptr->len);
+ ptr->valid = true;
+}
+
+#define store_subr(mp) cs_store(mp,true)
+#define store_cs(mp) cs_store(mp,false)
+
+#define CC_STACK_SIZE 24
+
+static double cc_stack[CC_STACK_SIZE], *stack_ptr = cc_stack;
+static cc_entry cc_tab[CS_MAX];
+static boolean is_cc_init = false;
+
+
+#define cc_pop(N) \
+ if (stack_ptr - cc_stack < (N)) \
+ stack_error(N); \
+ stack_ptr -= N
+
+#define stack_error(N) { \
+ char s[256]; \
+ mp_snprintf(s,255,"CharString: invalid access (%i) to stack (%i entries)", \
+ (int) N, (int)(stack_ptr - cc_stack)); \
+ mp_warn(mp,s); \
+ goto cs_error; \
+}
+
+
+#define cc_get(N) ((N) < 0 ? *(stack_ptr + (N)) : *(cc_stack + (N)))
+
+#define cc_push(V) *stack_ptr++ = (double)(V)
+#define cc_clear() stack_ptr = cc_stack
+
+#define set_cc(N, B, A, C) \
+ cc_tab[N].nargs = A; \
+ cc_tab[N].bottom = B; \
+ cc_tab[N].clear = C; \
+ cc_tab[N].valid = true
+
+static void cc_init (void) {
+ int i;
+ if (is_cc_init)
+ return;
+ for (i = 0; i < CS_MAX; i++)
+ cc_tab[i].valid = false;
+ set_cc (CS_HSTEM, true, 2, true);
+ set_cc (CS_VSTEM, true, 2, true);
+ set_cc (CS_VMOVETO, true, 1, true);
+ set_cc (CS_RLINETO, true, 2, true);
+ set_cc (CS_HLINETO, true, 1, true);
+ set_cc (CS_VLINETO, true, 1, true);
+ set_cc (CS_RRCURVETO, true, 6, true);
+ set_cc (CS_CLOSEPATH, false, 0, true);
+ set_cc (CS_CALLSUBR, false, 1, false);
+ set_cc (CS_RETURN, false, 0, false);
+ /*
+ |set_cc(CS_ESCAPE, false, 0, false);|
+ */
+ set_cc (CS_HSBW, true, 2, true);
+ set_cc (CS_ENDCHAR, false, 0, true);
+ set_cc (CS_RMOVETO, true, 2, true);
+ set_cc (CS_HMOVETO, true, 1, true);
+ set_cc (CS_VHCURVETO, true, 4, true);
+ set_cc (CS_HVCURVETO, true, 4, true);
+ set_cc (CS_DOTSECTION, false, 0, true);
+ set_cc (CS_VSTEM3, true, 6, true);
+ set_cc (CS_HSTEM3, true, 6, true);
+ set_cc (CS_SEAC, true, 5, true);
+ set_cc (CS_SBW, true, 4, true);
+ set_cc (CS_DIV, false, 2, false);
+ set_cc (CS_CALLOTHERSUBR, false, 0, false);
+ set_cc (CS_POP, false, 0, false);
+ set_cc (CS_SETCURRENTPOINT, true, 2, true);
+ is_cc_init = true;
+}
+
+@
+
+@d cs_getchar(mp) cdecrypt(*data++, &cr)
+
+@d mark_subr(mp,n) cs_mark(mp,0, n)
+@d mark_cs(mp,s) cs_mark(mp,s, 0)
+@d SMALL_BUF_SIZE 256
+
+@c
+static void cs_warn (MP mp, const char *cs_name, int subr, const char *fmt, ...) {
+ char buf[SMALL_BUF_SIZE];
+ char s[300];
+ va_list args;
+ va_start (args, fmt);
+ @= /*@@-bufferoverflowhigh@@*/ @>
+ (void)vsprintf (buf, fmt, args);
+ @= /*@@=bufferoverflowhigh@@*/ @>
+ va_end (args);
+ if (cs_name == NULL) {
+ mp_snprintf(s,299,"Subr (%i): %s", (int) subr, buf);
+ } else {
+ mp_snprintf(s,299,"CharString (/%s): %s", cs_name, buf);
+ }
+ mp_warn(mp,s);
+}
+
+static void cs_mark (MP mp, const char *cs_name, int subr)
+{
+ byte *data;
+ int i, b, cs_len;
+ integer a, a1, a2;
+ unsigned short cr;
+ static integer lastargOtherSubr3 = 3; /* the argument of last call to
+ OtherSubrs[3] */
+ cs_entry *ptr;
+ cc_entry *cc;
+ if (cs_name == NULL) {
+ check_subr (subr);
+ ptr = mp->ps->subr_tab + subr;
+ if (!ptr->valid)
+ return;
+ } else {
+ if (mp->ps->cs_notdef != NULL &&
+ (cs_name == notdef || strcmp (cs_name, notdef) == 0))
+ ptr = mp->ps->cs_notdef;
+ else {
+ for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
+ if (strcmp (ptr->glyph_name, cs_name) == 0)
+ break;
+ if (ptr == mp->ps->cs_ptr) {
+ char s[128];
+ mp_snprintf (s,128,"glyph `%s' undefined", cs_name);
+ mp_warn(mp,s);
+ return;
+ }
+ if (ptr->glyph_name == notdef)
+ mp->ps->cs_notdef = ptr;
+ }
+ }
+ /* only marked CharString entries and invalid entries can be skipped;
+ valid marked subrs must be parsed to keep the stack in sync */
+ if (!ptr->valid || (ptr->is_used && cs_name != NULL))
+ return;
+ ptr->is_used = true;
+ cr = 4330;
+ cs_len = (int)ptr->cslen;
+ data = ptr->data + 4;
+ for (i = 0; i < mp->ps->t1_lenIV; i++, cs_len--)
+ (void)cs_getchar (mp);
+ while (cs_len > 0) {
+ --cs_len;
+ b = cs_getchar (mp);
+ if (b >= 32) {
+ if (b <= 246)
+ a = b - 139;
+ else if (b <= 250) {
+ --cs_len;
+ a = (int)((unsigned)(b - 247) << 8) + 108 + cs_getchar (mp);
+ } else if (b <= 254) {
+ --cs_len;
+ a = -(int)((unsigned)(b - 251) << 8) - 108 - cs_getchar (mp);
+ } else {
+ cs_len -= 4;
+ a = (cs_getchar (mp) & 0xff) << 24;
+ a |= (cs_getchar (mp) & 0xff) << 16;
+ a |= (cs_getchar (mp) & 0xff) << 8;
+ a |= (cs_getchar (mp) & 0xff) << 0;
+ if (sizeof (integer) > 4 && (a & 0x80000000))
+ a |= ~0x7FFFFFFF;
+ }
+ cc_push (a);
+ } else {
+ if (b == CS_ESCAPE) {
+ b = cs_getchar (mp) + CS_1BYTE_MAX;
+ cs_len--;
+ }
+ if (b >= CS_MAX) {
+ cs_warn (mp,cs_name, subr, "command value out of range: %i",
+ (int) b);
+ goto cs_error;
+ }
+ cc = cc_tab + b;
+ if (!cc->valid) {
+ cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
+ goto cs_error;
+ }
+ if (cc->bottom) {
+ if (stack_ptr - cc_stack < cc->nargs)
+ cs_warn (mp,cs_name, subr,
+ "less arguments on stack (%i) than required (%i)",
+ (int) (stack_ptr - cc_stack), (int) cc->nargs);
+ else if (stack_ptr - cc_stack > cc->nargs)
+ cs_warn (mp,cs_name, subr,
+ "more arguments on stack (%i) than required (%i)",
+ (int) (stack_ptr - cc_stack), (int) cc->nargs);
+ }
+ switch (cc - cc_tab) {
+ case CS_CALLSUBR:
+ a1 = (integer)cc_get (-1);
+ cc_pop (1);
+ mark_subr (mp,a1);
+ if (!mp->ps->subr_tab[a1].valid) {
+ cs_warn (mp,cs_name, subr, "cannot call subr (%i)", (int) a1);
+ goto cs_error;
+ }
+ break;
+ case CS_DIV:
+ cc_pop (2);
+ cc_push (0);
+ break;
+ case CS_CALLOTHERSUBR:
+ a1 = (integer)cc_get (-1);
+ if (a1 == 3)
+ lastargOtherSubr3 = (integer)cc_get (-3);
+ a1 = (integer)cc_get (-2) + 2;
+ cc_pop (a1);
+ break;
+ case CS_POP:
+ cc_push (lastargOtherSubr3);
+ /* the only case when we care about the value being pushed onto
+ stack is when POP follows CALLOTHERSUBR (changing hints by
+ OtherSubrs[3])
+ */
+ break;
+ case CS_SEAC:
+ a1 = (integer)cc_get (3);
+ a2 = (integer)cc_get (4);
+ cc_clear ();
+ mark_cs (mp,standard_glyph_names[a1]);
+ mark_cs (mp,standard_glyph_names[a2]);
+ break;
+ default:
+ if (cc->clear)
+ cc_clear ();
+ }
+ }
+ }
+ return;
+ cs_error: /* an error occured during parsing */
+ cc_clear ();
+ ptr->valid = false;
+ ptr->is_used = false;
+}
+
+static void t1_subset_ascii_part (MP mp, font_number tex_font, fm_entry *fm_cur)
+{
+ int i, j;
+ t1_getline (mp);
+ while (!t1_prefix ("/Encoding")) {
+ t1_scan_param (mp,tex_font, fm_cur);
+ t1_putline (mp);
+ t1_getline (mp);
+ }
+ t1_builtin_enc (mp);
+ if (is_reencoded (fm_cur))
+ mp->ps->t1_glyph_names = external_enc ();
+ else
+ mp->ps->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
+ /*
+ |if (is_included (fm_cur) && is_subsetted (fm_cur)) {
+ make_subset_tag (fm_cur, t1_glyph_names, tex_font);
+ update_subset_tag ();
+ }|
+ */
+ if ((!is_subsetted (fm_cur)) && mp->ps->t1_encoding == ENC_STANDARD)
+ t1_puts (mp,"/Encoding StandardEncoding def\n");
+ else {
+ t1_puts
+ (mp,"/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
+ for (i = 0, j = 0; i < 256; i++) {
+ if (is_used_char (i) && mp->ps->t1_glyph_names[i] != notdef) {
+ j++;
+ mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
+ "dup %i /%s put\n", (int) t1_char (i),
+ mp->ps->t1_glyph_names[i]);
+ t1_puts(mp,mp->ps->t1_line_array);
+ }
+ }
+ /* We didn't mark anything for the Encoding array. */
+ /* We add "dup 0 /.notdef put" for compatibility */
+ /* with Acrobat 5.0. */
+ if (j == 0)
+ t1_puts (mp,"dup 0 /.notdef put\n");
+ t1_puts (mp,"readonly def\n");
+ }
+ do {
+ t1_getline (mp);
+ t1_scan_param (mp,tex_font, fm_cur);
+ if (!t1_prefix ("/UniqueID")) /* ignore UniqueID for subsetted fonts */
+ t1_putline (mp);
+ }
+ while (mp->ps->t1_in_eexec == 0);
+}
+
+#define t1_subr_flush(mp) t1_flush_cs(mp,true)
+#define t1_cs_flush(mp) t1_flush_cs(mp,false)
+
+static void cs_init (MP mp) {
+ mp->ps->cs_ptr = mp->ps->cs_tab = NULL;
+ mp->ps->cs_dict_start = mp->ps->cs_dict_end = NULL;
+ mp->ps->cs_count = mp->ps->cs_size = mp->ps->cs_size_pos = 0;
+ mp->ps->cs_token_pair = NULL;
+ mp->ps->subr_tab = NULL;
+ mp->ps->subr_array_start = mp->ps->subr_array_end = NULL;
+ mp->ps->subr_max = mp->ps->subr_size = mp->ps->subr_size_pos = 0;
+}
+
+static void init_cs_entry ( cs_entry * cs) {
+ cs->data = NULL;
+ cs->glyph_name = NULL;
+ cs->len = 0;
+ cs->cslen = 0;
+ cs->is_used = false;
+ cs->valid = false;
+}
+
+static void t1_mark_glyphs (MP mp, font_number tex_font);
+
+static void t1_read_subrs (MP mp, font_number tex_font, fm_entry *fm_cur, int read_only)
+{
+ int i, s;
+ cs_entry *ptr;
+ t1_getline (mp);
+ while (!(t1_charstrings () || t1_subrs ())) {
+ t1_scan_param (mp,tex_font, fm_cur);
+ if (!read_only)
+ t1_putline (mp);
+ t1_getline (mp);
+ }
+ FOUND:
+ mp->ps->t1_cs = true;
+ mp->ps->t1_scan = false;
+ if (!t1_subrs ())
+ return;
+ mp->ps->subr_size_pos = (int)(strlen ("/Subrs") + 1);
+ /* |subr_size_pos| points to the number indicating dict size after "/Subrs" */
+ mp->ps->subr_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->subr_size_pos, 0);
+ if (mp->ps->subr_size == 0) {
+ while (!t1_charstrings ())
+ t1_getline (mp);
+ return;
+ }
+ /* |subr_tab = xtalloc (subr_size, cs_entry);| */
+ mp->ps->subr_tab = (cs_entry *)mp_xmalloc (mp,(size_t)mp->ps->subr_size, sizeof (cs_entry));
+ for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
+ init_cs_entry (ptr);
+ mp->ps->subr_array_start = mp_xstrdup (mp,mp->ps->t1_line_array);
+ t1_getline (mp);
+ while (mp->ps->t1_cslen) {
+ store_subr (mp);
+ t1_getline (mp);
+ }
+ /* mark the first four entries without parsing */
+ for (i = 0; i < mp->ps->subr_size && i < 4; i++)
+ mp->ps->subr_tab[i].is_used = true;
+ /* the end of the Subrs array might have more than one line so we need to
+ concatnate them to |subr_array_end|. Unfortunately some fonts don't have
+ the Subrs array followed by the CharStrings dict immediately (synthetic
+ fonts). If we cannot find CharStrings in next |POST_SUBRS_SCAN| lines then
+ we will treat the font as synthetic and ignore everything until next
+ Subrs is found
+ */
+#define POST_SUBRS_SCAN 5
+ s = 0;
+ *mp->ps->t1_buf_array = 0;
+ for (i = 0; i < POST_SUBRS_SCAN; i++) {
+ if (t1_charstrings ())
+ break;
+ s += mp->ps->t1_line_ptr - mp->ps->t1_line_array;
+ alloc_array (t1_buf, s, T1_BUF_SIZE);
+ strcat (mp->ps->t1_buf_array, mp->ps->t1_line_array);
+ t1_getline (mp);
+ }
+ mp->ps->subr_array_end = mp_xstrdup (mp,mp->ps->t1_buf_array);
+ if (i == POST_SUBRS_SCAN) { /* CharStrings not found;
+ suppose synthetic font */
+ for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
+ if (ptr->valid)
+ mp_xfree (ptr->data);
+ mp_xfree (mp->ps->subr_tab);
+ mp_xfree (mp->ps->subr_array_start);
+ mp_xfree (mp->ps->subr_array_end);
+ cs_init (mp);
+ mp->ps->t1_cs = false;
+ mp->ps->t1_synthetic = true;
+ while (!(t1_charstrings () || t1_subrs ()))
+ t1_getline (mp);
+ goto FOUND;
+ }
+}
+
+@ @c
+static void t1_flush_cs (MP mp, boolean is_subr)
+{
+ char *p;
+ byte *r, *return_cs = NULL;
+ cs_entry *tab, *end_tab, *ptr;
+ char *start_line, *line_end;
+ int count, size_pos;
+ unsigned short cr, cs_len = 0; /* to avoid warning about uninitialized use of |cs_len| */
+ if (is_subr) {
+ start_line = mp->ps->subr_array_start;
+ line_end = mp->ps->subr_array_end;
+ size_pos = mp->ps->subr_size_pos;
+ tab = mp->ps->subr_tab;
+ count = mp->ps->subr_max + 1;
+ end_tab = mp->ps->subr_tab + count;
+ } else {
+ start_line = mp->ps->cs_dict_start;
+ line_end = mp->ps->cs_dict_end;
+ size_pos = mp->ps->cs_size_pos;
+ tab = mp->ps->cs_tab;
+ end_tab = mp->ps->cs_ptr;
+ count = mp->ps->cs_count;
+ }
+ mp->ps->t1_line_ptr = mp->ps->t1_line_array;
+ for (p = start_line; p - start_line < size_pos;)
+ *mp->ps->t1_line_ptr++ = *p++;
+ while (mp_isdigit (*p))
+ p++;
+ mp_snprintf (mp->ps->t1_line_ptr, (int)mp->ps->t1_line_limit, "%u", (unsigned)count);
+ strcat (mp->ps->t1_line_ptr, p);
+ mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
+ t1_putline (mp);
+
+ /* create |return_cs| to replace unsused subr's */
+ if (is_subr) {
+ cr = 4330;
+ cs_len = 0;
+ return_cs = mp_xmalloc (mp, (size_t)(mp->ps->t1_lenIV + 1) , sizeof(byte));
+ if ( mp->ps->t1_lenIV >= 0) {
+ for (cs_len = 0, r = return_cs;
+ cs_len<(unsigned short)mp->ps->t1_lenIV; cs_len++, r++)
+ *r = cencrypt (0x00, &cr);
+ *r = cencrypt (CS_RETURN, &cr);
+ } else {
+ *return_cs = CS_RETURN;
+ }
+ cs_len++;
+ }
+
+ for (ptr = tab; ptr < end_tab; ptr++) {
+ if (ptr->is_used) {
+ if (is_subr)
+ mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
+ "dup %i %u", (int) (ptr - tab), ptr->cslen);
+ else
+ mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
+ "/%s %u", ptr->glyph_name, ptr->cslen);
+ p = strend (mp->ps->t1_line_array);
+ memcpy (p, ptr->data, (size_t)ptr->len);
+ mp->ps->t1_line_ptr = p + ptr->len;
+ t1_putline (mp);
+ } else {
+ /* replace unsused subr's by |return_cs| */
+ if (is_subr) {
+ mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
+ "dup %i %u%s ", (int) (ptr - tab),
+ cs_len, mp->ps->cs_token_pair[0]);
+ p = strend (mp->ps->t1_line_array);
+ memcpy (p, return_cs, (size_t)cs_len);
+ mp->ps->t1_line_ptr = p + cs_len;
+ t1_putline (mp);
+ mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit,
+ " %s", mp->ps->cs_token_pair[1]);
+ mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
+ t1_putline (mp);
+ }
+ }
+ mp_xfree (ptr->data);
+ if (ptr->glyph_name != notdef)
+ mp_xfree (ptr->glyph_name);
+ }
+ mp_snprintf (mp->ps->t1_line_array, (int)mp->ps->t1_line_limit, "%s", line_end);
+ mp->ps->t1_line_ptr = eol (mp->ps->t1_line_array);
+ t1_putline (mp);
+ if (is_subr)
+ mp_xfree (return_cs);
+ mp_xfree (tab);
+ mp_xfree (start_line);
+ mp_xfree (line_end);
+}
+
+static void t1_mark_glyphs (MP mp, font_number tex_font)
+{
+ int i;
+ char *charset = extra_charset ();
+ char *g, *s, *r;
+ cs_entry *ptr;
+ if (mp->ps->t1_synthetic || embed_all_glyphs (tex_font)) { /* mark everything */
+ if (mp->ps->cs_tab != NULL)
+ for (ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
+ if (ptr->valid)
+ ptr->is_used = true;
+ if (mp->ps->subr_tab != NULL) {
+ for (ptr = mp->ps->subr_tab; ptr - mp->ps->subr_tab < mp->ps->subr_size; ptr++)
+ if (ptr->valid)
+ ptr->is_used = true;
+ mp->ps->subr_max = mp->ps->subr_size - 1;
+ }
+ return;
+ }
+ mark_cs (mp,notdef);
+ for (i = 0; i < 256; i++)
+ if (is_used_char (i)) {
+ if (mp->ps->t1_glyph_names[i] == notdef) {
+ char S[128];
+ mp_snprintf(S,128, "character %i is mapped to %s", i, notdef);
+ mp_warn(mp,S);
+ } else
+ mark_cs (mp,mp->ps->t1_glyph_names[i]);
+ }
+ if (charset == NULL)
+ goto SET_SUBR_MAX;
+ g = s = charset + 1; /* skip the first '/' */
+ r = strend (g);
+ while (g < r) {
+ while (*s != '/' && s < r)
+ s++;
+ *s = 0; /* terminate g by rewriting '/' to 0 */
+ mark_cs (mp,g);
+ g = s + 1;
+ }
+ SET_SUBR_MAX:
+ if (mp->ps->subr_tab != NULL)
+ for (mp->ps->subr_max = -1, ptr = mp->ps->subr_tab;
+ ptr - mp->ps->subr_tab < mp->ps->subr_size;
+ ptr++)
+ if (ptr->is_used && ptr - mp->ps->subr_tab > mp->ps->subr_max)
+ mp->ps->subr_max = ptr - mp->ps->subr_tab;
+}
+
+static void t1_do_subset_charstrings (MP mp, font_number tex_font)
+{
+ cs_entry *ptr;
+ mp->ps->cs_size_pos =
+ strstr (mp->ps->t1_line_array, charstringname) + strlen (charstringname)
+ - mp->ps->t1_line_array + 1;
+ /* |cs_size_pos| points to the number indicating
+ dict size after "/CharStrings" */
+ mp->ps->cs_size = (int)t1_scan_num (mp,mp->ps->t1_line_array + mp->ps->cs_size_pos, 0);
+ mp->ps->cs_ptr = mp->ps->cs_tab = mp_xmalloc (mp,(size_t)mp->ps->cs_size, sizeof(cs_entry));
+ for (ptr = mp->ps->cs_tab; ptr - mp->ps->cs_tab < mp->ps->cs_size; ptr++)
+ init_cs_entry (ptr);
+ mp->ps->cs_notdef = NULL;
+ mp->ps->cs_dict_start = mp_xstrdup (mp,mp->ps->t1_line_array);
+ t1_getline (mp);
+ while (mp->ps->t1_cslen) {
+ store_cs (mp);
+ t1_getline (mp);
+ }
+ mp->ps->cs_dict_end = mp_xstrdup (mp,mp->ps->t1_line_array);
+ t1_mark_glyphs (mp,tex_font);
+}
+
+static void t1_subset_charstrings (MP mp, font_number tex_font)
+{
+ cs_entry *ptr;
+ t1_do_subset_charstrings (mp, tex_font);
+ if (mp->ps->subr_tab != NULL) {
+ if (mp->ps->cs_token_pair == NULL)
+ mp_fatal_error
+ (mp, "This Type 1 font uses mismatched subroutine begin/end token pairs.");
+ t1_subr_flush (mp);
+ }
+ for (mp->ps->cs_count = 0, ptr = mp->ps->cs_tab; ptr < mp->ps->cs_ptr; ptr++)
+ if (ptr->is_used)
+ mp->ps->cs_count++;
+ t1_cs_flush (mp);
+}
+
+static void t1_subset_end (MP mp)
+{
+ if (mp->ps->t1_synthetic) { /* copy to "dup /FontName get exch definefont pop" */
+ while (!strstr (mp->ps->t1_line_array, "definefont")) {
+ t1_getline (mp);
+ t1_putline (mp);
+ }
+ while (!t1_end_eexec ())
+ t1_getline (mp); /* ignore the rest */
+ t1_putline (mp); /* write "mark currentfile closefile" */
+ } else
+ while (!t1_end_eexec ()) { /* copy to "mark currentfile closefile" */
+ t1_getline (mp);
+ t1_putline (mp);
+ }
+ t1_stop_eexec (mp);
+ if (fixedcontent) { /* copy 512 zeros (not needed for PDF) */
+ while (!t1_cleartomark ()) {
+ t1_getline (mp);
+ t1_putline (mp);
+ }
+ if (!mp->ps->t1_synthetic) /* don't check "{restore}if" for synthetic fonts */
+ t1_check_end (mp); /* write "{restore}if" if found */
+ }
+}
+
+static int t1_updatefm (MP mp, font_number f, fm_entry *fm)
+{
+ char *s, *p;
+ mp->ps->read_encoding_only = true;
+ if (!t1_open_fontfile (mp,fm,NULL)) {
+ return 0;
+ }
+ t1_scan_only (mp,f, fm);
+ s = mp_xstrdup(mp,mp->ps->fontname_buf);
+ p = s;
+ while (*p != ' ' && *p != 0)
+ p++;
+ *p=0;
+ fm->ps_name = s;
+ t1_close_font_file (mp,"");
+ return 1;
+}
+
+
+static void writet1 (MP mp, font_number tex_font, fm_entry *fm_cur) {
+ unsigned save_selector = mp->selector;
+ mp_normalize_selector(mp);
+ mp->ps->read_encoding_only = false;
+ if (!is_included (fm_cur)) { /* scan parameters from font file */
+ if (!t1_open_fontfile (mp,fm_cur,"{"))
+ return;
+ t1_scan_only (mp,tex_font, fm_cur);
+ t1_close_font_file (mp,"}");
+ return;
+ }
+ if (!is_subsetted (fm_cur)) { /* include entire font */
+ if (!t1_open_fontfile (mp,fm_cur,"<<"))
+ return;
+ t1_include (mp,tex_font,fm_cur);
+ t1_close_font_file (mp,">>");
+ return;
+ }
+ /* partial downloading */
+ if (!t1_open_fontfile (mp,fm_cur,"<"))
+ return;
+ t1_subset_ascii_part (mp,tex_font,fm_cur);
+ t1_start_eexec (mp,fm_cur);
+ cc_init ();
+ cs_init (mp);
+ t1_read_subrs (mp,tex_font, fm_cur, false);
+ t1_subset_charstrings (mp,tex_font);
+ t1_subset_end (mp);
+ t1_close_font_file (mp,">");
+ mp->selector = save_selector;
+}
+
+@ @<Declarations@>=
+static void t1_free (MP mp);
+
+@ @c
+static void t1_free (MP mp) {
+ mp_xfree (mp->ps->t1_line_array);
+ mp_xfree (mp->ps->t1_buf_array);
+}
+
+@* Embedding Charstrings.
+
+The SVG backend uses some routines that use an ascii representation of
+a type1 font. First, here is the type associated with it:
+
+@<Types ...@>=
+typedef struct mp_ps_font {
+ int font_num; /* just to put something in */
+ char **t1_glyph_names;
+ cs_entry *cs_tab;
+ cs_entry *cs_ptr;
+ cs_entry *subr_tab;
+ int t1_lenIV;
+ int slant;
+ int extend;
+ @<Variables for the charstring parser@>
+} mp_ps_font;
+
+@ The parser creates a structure and fills it.
+
+@c
+
+mp_ps_font *mp_ps_font_parse (MP mp, int tex_font) {
+ mp_ps_font *f;
+ fm_entry *fm_cur;
+ char msg[128];
+ (void)mp_has_fm_entry (mp, tex_font, &fm_cur);
+ if (fm_cur == NULL) {
+ mp_snprintf(msg,128,"fontmap entry for `%s' not found", mp->font_name[tex_font]);
+ mp_warn(mp,msg);
+ return NULL;
+ }
+ if (is_truetype(fm_cur) ||
+ (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL) ||
+ (!is_included(fm_cur))) {
+ mp_snprintf(msg,128,"font `%s' cannot be embedded", mp->font_name[tex_font]);
+ mp_warn(mp,msg);
+ return NULL;
+ }
+ if (!t1_open_fontfile (mp,fm_cur,"<")) { /* message handled there */
+ return NULL;
+ }
+ f = mp_xmalloc(mp, 1, sizeof(struct mp_ps_font));
+ f->font_num = tex_font;
+ f->t1_glyph_names = NULL;
+ f->cs_tab = NULL;
+ f->cs_ptr = NULL;
+ f->subr_tab = NULL;
+ f->orig_x = f->orig_y = 0.0;
+ f->slant = (int)fm_cur->slant;
+ f->extend = (int)fm_cur->extend;
+ t1_getline (mp);
+ while (!t1_prefix ("/Encoding")) {
+ t1_scan_param (mp,tex_font, fm_cur);
+ t1_getline (mp);
+ }
+ t1_builtin_enc (mp);
+ if (is_reencoded (fm_cur)) {
+ mp_read_enc (mp, fm_cur->encoding);;
+ f->t1_glyph_names = external_enc ();
+ } else {
+ f->t1_glyph_names = mp->ps->t1_builtin_glyph_names;
+ }
+ do {
+ t1_getline (mp);
+ t1_scan_param (mp,tex_font, fm_cur);
+ } while (mp->ps->t1_in_eexec == 0);
+
+ /* t1_start_eexec (mp,fm_cur); */
+ cc_init ();
+ cs_init (mp);
+ /* the boolean is needed to make sure that |t1_read_subrs|
+ doesn't output stuff */
+ t1_read_subrs (mp,tex_font, fm_cur, true);
+ mp->ps->t1_synthetic = true ;
+ t1_do_subset_charstrings (mp, tex_font);
+ f->cs_tab = mp->ps->cs_tab;
+ mp->ps->cs_tab = NULL;
+ f->cs_ptr = mp->ps->cs_ptr;
+ mp->ps->cs_ptr = NULL;
+ f->subr_tab = mp->ps->subr_tab;
+ mp->ps->subr_tab = NULL;
+ f->t1_lenIV = mp->ps->t1_lenIV;
+ t1_close_font_file (mp,">");
+ return f;
+}
+
+@ @<Exported function headers@>=
+mp_ps_font *mp_ps_font_parse (MP mp, int tex_font);
+
+@ Freeing the structure
+
+@c
+void mp_ps_font_free (MP mp, mp_ps_font *f) {
+ (void)mp;
+ mp_xfree(f);
+}
+
+@ @<Exported function headers@>=
+void mp_ps_font_free (MP mp, mp_ps_font *f);
+
+
+@ Parsing Charstrings.
+
+@<Variables for the charstring parser@>=
+double cur_x, cur_y; /* current point */
+double orig_x, orig_y; /* origin (for seac) */
+mp_edge_object *h; /* the whole picture */
+mp_graphic_object *p; /* the current subpath in the picture */
+mp_knot *pp; /* the last known knot in the subpath */
+
+
+@ @c
+mp_edge_object *mp_ps_font_charstring (MP mp, mp_ps_font *f, int c) {
+ mp_edge_object *h = NULL;
+ f->h = NULL; f->p = NULL; f->pp = NULL; /* just in case */
+ f->cur_x = f->cur_y = 0.0;
+ f->orig_x = f->orig_y = 0.0;
+ if (cs_parse(mp,f,f->t1_glyph_names[c], 0)) {
+ h = f->h;
+ } else {
+ if (f->h != NULL) {
+ finish_subpath();
+ mp_gr_toss_objects(f->h);
+ }
+ }
+ f->h = NULL; f->p = NULL; f->pp = NULL;
+ return h;
+}
+
+
+
+@ @<Exported function headers@>=
+mp_edge_object *mp_ps_font_charstring (MP mp, mp_ps_font *f, int c);
+
+
+@
+@<Declarations@>=
+boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr);
+
+@
+@d scaled_from_double(a) (scaled)((a)*65536.0)
+@d double_from_scaled(a) (double)((a)/65536.0)
+
+@d start_subpath(f,dx,dy) do {
+ assert(f->pp == NULL);
+ assert(f->p == NULL);
+ f->pp = mp_xmalloc(mp, 1, sizeof (mp_knot));
+ f->pp->left_type = mp_endpoint;
+ f->pp->right_type = mp_open;
+ f->pp->x_coord = scaled_from_double(f->cur_x + dx);
+ f->pp->y_coord = scaled_from_double(f->cur_y + dy);
+ f->pp->left_x = f->pp->right_x = f->pp->x_coord;
+ f->pp->left_y = f->pp->right_y = f->pp->y_coord;
+ f->pp->next = NULL;
+ f->cur_x += dx;
+ f->cur_y += dy;
+ f->p = mp_new_graphic_object(mp,mp_fill_code);
+ gr_path_p((mp_fill_object *)f->p) = f->pp;
+} while (0)
+
+@d finish_subpath() do {
+ if (f->p != NULL) {
+ if (f->h->body == NULL) {
+ f->h->body = f->p;
+ } else {
+ mp_graphic_object *q = f->h->body;
+ while (gr_link(q) != NULL)
+ q = gr_link(q);
+ q->next = f->p;
+ }
+ }
+ f->p = NULL;
+ f->pp = NULL;
+} while (0)
+
+@d add_line_segment(f,dx,dy) do {
+ assert(f->pp != NULL);
+ n = mp_xmalloc(mp,1, sizeof (mp_knot));
+ n->left_type = mp_open;
+ n->right_type = mp_endpoint;
+ n->next = gr_path_p((mp_fill_object *)f->p); /* loop */
+ n->x_coord = scaled_from_double(f->cur_x + dx);
+ n->y_coord = scaled_from_double(f->cur_y + dy);
+ n->right_x = n->x_coord;
+ n->right_y = n->y_coord;
+ n->left_x = n->x_coord;
+ n->left_y = n->y_coord;
+ f->pp->right_type = mp_open;
+ f->pp->next = n;
+ f->pp = n;
+ f->cur_x += dx;
+ f->cur_y += dy;
+} while (0)
+
+@d add_curve_segment(f,dx1,dy1,dx2,dy2,dx3,dy3) do {
+ n = mp_xmalloc(mp, 1, sizeof (mp_knot));
+ n->left_type = mp_open;
+ n->right_type = mp_endpoint;
+ n->next = gr_path_p((mp_fill_object *)f->p); /* loop */
+ n->x_coord = scaled_from_double(f->cur_x + dx1 + dx2 + dx3);
+ n->y_coord = scaled_from_double(f->cur_y + dy1 + dy2 + dy3);
+ n->right_x = n->x_coord;
+ n->right_y = n->y_coord;
+ n->left_x = scaled_from_double(f->cur_x + dx1 + dx2);
+ n->left_y = scaled_from_double(f->cur_y + dy1 + dy2);
+ f->pp->right_x = scaled_from_double(f->cur_x + dx1);
+ f->pp->right_y = scaled_from_double(f->cur_y + dy1);
+ f->pp->right_type = mp_open;
+ f->pp->next = n;
+ f->pp = n;
+ f->cur_x += dx1 + dx2 + dx3;
+ f->cur_y += dy1 + dy2 + dy3;
+} while (0)
+
+@d cs_no_debug(A) cs_do_debug(mp,f,A,#A)
+@d cs_debug(A)
+
+@c
+
+void cs_do_debug (MP mp, mp_ps_font *f, int i, char *s) {
+ int n = cc_tab[i].nargs;
+ (void)mp; /* for -Wall */
+ (void)f; /* for -Wall */
+ while (n>0) {
+ fprintf (stdout,"%d ", (int)cc_get((-n)));
+ n--;
+ }
+ fprintf (stdout,"%s\n", s);
+}
+
+boolean cs_parse (MP mp, mp_ps_font *f, const char *cs_name, int subr)
+{
+ byte *data;
+ int i, b, cs_len;
+ integer a, a1, a2;
+ unsigned short cr;
+ static integer lastargOtherSubr3 = 3;
+
+ cs_entry *ptr;
+ cc_entry *cc;
+ mp_knot *n;
+
+ if (cs_name == NULL) {
+ ptr = f->subr_tab + subr;
+ } else {
+ i = 0;
+ for (ptr = f->cs_tab; ptr < f->cs_ptr; ptr++, i++) {
+ if (strcmp (ptr->glyph_name, cs_name) == 0)
+ break;
+ }
+ ptr = f->cs_tab+i; /* this is the right charstring */
+ }
+
+ data = ptr->data + 4;
+ cr = 4330;
+ cs_len = (int)ptr->cslen;
+ for (i = 0; i < f->t1_lenIV; i++, cs_len--)
+ (void)cs_getchar (mp);
+
+ while (cs_len > 0) {
+ --cs_len;
+ b = cs_getchar(mp);
+ if (b >= 32) {
+ if (b <= 246)
+ a = b - 139;
+ else if (b <= 250) {
+ --cs_len;
+ a = (int)((unsigned)(b - 247) << 8) + 108 + cs_getchar (mp);
+ } else if (b <= 254) {
+ --cs_len;
+ a = -(int)((unsigned)(b - 251) << 8) - 108 - cs_getchar (mp);
+ } else {
+ cs_len -= 4;
+ a = (cs_getchar (mp) & 0xff) << 24;
+ a |= (cs_getchar (mp) & 0xff) << 16;
+ a |= (cs_getchar (mp) & 0xff) << 8;
+ a |= (cs_getchar (mp) & 0xff) << 0;
+ if (sizeof (integer) > 4 && (a & 0x80000000))
+ a |= ~0x7FFFFFFF;
+ }
+ cc_push (a);
+ } else {
+ if (b == CS_ESCAPE) {
+ b = cs_getchar (mp) + CS_1BYTE_MAX;
+ cs_len--;
+ }
+ if (b >= CS_MAX) {
+ cs_warn (mp,cs_name, subr, "command value out of range: %i",
+ (int) b);
+ goto cs_error;
+ }
+ cc = cc_tab + b;
+ if (!cc->valid) {
+ cs_warn (mp,cs_name, subr, "command not valid: %i", (int) b);
+ goto cs_error;
+ }
+ if (cc->bottom) {
+ if (stack_ptr - cc_stack < cc->nargs)
+ cs_warn (mp,cs_name, subr,
+ "less arguments on stack (%i) than required (%i)",
+ (int) (stack_ptr - cc_stack), (int) cc->nargs);
+ else if (stack_ptr - cc_stack > cc->nargs)
+ cs_warn (mp,cs_name, subr,
+ "more arguments on stack (%i) than required (%i)",
+ (int) (stack_ptr - cc_stack), (int) cc->nargs);
+ }
+ switch (cc - cc_tab) {
+ case CS_CLOSEPATH: /* - CLOSEPATH |- */
+ cs_debug(CS_CLOSEPATH);
+ finish_subpath();
+ cc_clear ();
+ break;
+ case CS_HLINETO: /* |- dx HLINETO |- */
+ cs_debug(CS_HLINETO);
+ add_line_segment(f,cc_get(-1),0);
+ cc_clear ();
+ break;
+ case CS_HVCURVETO: /* |- dx1 dx2 dy2 dy3 HVCURVETO |- */
+ cs_debug(CS_HVCURVETO);
+ add_curve_segment(f,cc_get(-4),0,cc_get(-3),cc_get(-2),0,cc_get(-1));
+ cc_clear ();
+ break;
+ case CS_RLINETO: /* |- dx dy RLINETO |- */
+ cs_debug(CS_RLINETO);
+ add_line_segment(f,cc_get(-2),cc_get(-1));
+ cc_clear ();
+ break;
+ case CS_RRCURVETO: /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
+ cs_debug(CS_RRCURVETO);
+ add_curve_segment(f,cc_get(-6),cc_get(-5),cc_get(-4),cc_get(-3),cc_get(-2),cc_get(-1));
+ cc_clear ();
+ break;
+ case CS_VHCURVETO: /* |- dy1 dx2 dy2 dx3 VHCURVETO |- */
+ cs_debug(CS_VHCURVETO);
+ add_curve_segment(f,0, cc_get(-4),cc_get(-3),cc_get(-2),cc_get(-1),0);
+ cc_clear ();
+ break;
+ case CS_VLINETO: /* |- dy VLINETO |- */
+ cs_debug(CS_VLINETO);
+ add_line_segment(f,0,cc_get(-1));
+ cc_clear ();
+ break;
+ case CS_HMOVETO: /* |- dx HMOVETO |- */
+ cs_debug(CS_HMOVETO);
+ finish_subpath();
+ start_subpath(f,cc_get(-1),0);
+ cc_clear ();
+ break;
+ case CS_RMOVETO: /* |- dx dy RMOVETO |- */
+ cs_debug(CS_RMOVETO);
+ finish_subpath();
+ start_subpath(f,cc_get(-2),cc_get(-1));
+ cc_clear ();
+ break;
+ case CS_VMOVETO: /* |- dy VMOVETO |- */
+ cs_debug(CS_VMOVETO);
+ finish_subpath();
+ start_subpath(f,0,cc_get(-1));
+ cc_clear ();
+ break;
+ /* hinting commands */
+ case CS_DOTSECTION: /* - DOTSECTION |- */
+ cs_debug(CS_DOTSECTION);
+ cc_clear ();
+ break;
+ case CS_HSTEM: /* |- y dy HSTEM |- */
+ cs_debug(CS_HSTEM);
+ cc_clear ();
+ break;
+ case CS_HSTEM3: /* |- y0 dy0 y1 dy1 y2 dy2 HSTEM3 |- */
+ cs_debug(CS_HSTEM3);
+ cc_clear ();
+ break;
+ case CS_VSTEM: /* |- x dx VSTEM |- */
+ cs_debug(CS_VSTEM);
+ cc_clear ();
+ break;
+ case CS_VSTEM3: /* |- x0 dx0 x1 dx1 x2 dx2 VSTEM3 |- */
+ cs_debug(CS_VSTEM3);
+ cc_clear ();
+ break;
+ /* start and close commands */
+ case CS_SEAC: /* |- asb adx ady bchar achar SEAC |- */
+ cs_debug(CS_SEAC);
+ { double adx, ady;
+ adx = cc_get (1);
+ ady = cc_get (2);
+ a1 = (integer)cc_get (3);
+ a2 = (integer)cc_get (4);
+ cc_clear ();
+ (void)cs_parse(mp,f,standard_glyph_names[a1],0); /* base */
+ f->orig_x += adx;
+ f->orig_y += ady;
+ (void)cs_parse(mp,f,standard_glyph_names[a2],0);
+ }
+ break;
+ case CS_ENDCHAR: /* - ENDCHAR |- */
+ cs_debug(CS_ENDCHAR);
+ cc_clear ();
+ return true;
+ break;
+ case CS_HSBW: /* |- sbx wx HSBW |- */
+ cs_debug(CS_HSBW);
+ f->h = mp_xmalloc(mp, 1,sizeof(mp_edge_object));
+ f->h->body = NULL; f->h->next = NULL;
+ f->h->parent = mp;
+ f->h->filename = NULL;
+ f->h->minx = f->h->miny = f->h->maxx = f->h->maxy = 0;
+ f->cur_x = cc_get(-2) + f->orig_x;
+ f->cur_y = 0.0 + f->orig_y;
+ f->orig_x = f->cur_x;
+ f->orig_y = f->cur_y;
+ cc_clear ();
+ break;
+ case CS_SBW: /* |- sbx sby wx wy SBW |- */
+ cs_debug(CS_SBW);
+ f->h = mp_xmalloc(mp, 1,sizeof(mp_edge_object));
+ f->h->body = NULL; f->h->next = NULL;
+ f->h->parent = mp;
+ f->h->filename = NULL;
+ f->h->minx = f->h->miny = f->h->maxx = f->h->maxy = 0;
+ f->cur_x = cc_get(-4) + f->orig_x;
+ f->cur_y = cc_get(-3) + f->orig_y;
+ f->orig_x = f->cur_x;
+ f->orig_y = f->cur_y;
+ cc_clear ();
+ break;
+ /* arithmetic */
+ case CS_DIV: /* num1 num2 DIV quotient */
+ cs_debug(CS_DIV);
+ { double num,den,res;
+ num = cc_get (-2);
+ den = cc_get (-1);
+ res = num/den;
+ cc_pop (2);
+ cc_push (res);
+ break;
+ }
+ /* subrs */
+ case CS_CALLSUBR: /* subr CALLSUBR - */
+ cs_debug(CS_CALLSUBR);
+ a1 = (integer)cc_get (-1);
+ cc_pop (1);
+ (void)cs_parse(mp,f,NULL,a1);
+ break;
+ case CS_RETURN: /* - RETURN - */
+ cs_debug(CS_RETURN);
+ return true;
+ break;
+ case CS_CALLOTHERSUBR: /* arg1 ... argn n othersubr CALLOTHERSUBR - */
+ a1 = (integer)cc_get (-1);
+ if (a1 == 3)
+ lastargOtherSubr3 = (integer)cc_get (-3);
+ a1 = (integer)cc_get(-2) + 2;
+ cc_pop (a1);
+ break;
+ case CS_POP: /* - POP number */
+ cc_push (lastargOtherSubr3);
+ break;
+ case CS_SETCURRENTPOINT: /* |- x y SETCURRENTPOINT |- */
+ cs_debug(CS_SETCURRENTPOINT);
+ f->cur_x = cc_get(-2);
+ f->cur_y = cc_get(-1);
+ f->pp = NULL;
+ cc_clear ();
+ break;
+ default:
+ if (cc->clear)
+ cc_clear ();
+ }
+ }
+ }
+ return true;
+cs_error: /* an error occured during parsing */
+ cc_clear ();
+ ptr->valid = false;
+ ptr->is_used = false;
+ return false;
+}
+
+@* \[44d] Embedding fonts.
+
+@ The |tfm_num| is officially of type |font_number|, but that
+type does not exist yet at this point in the output order.
+
+@<Types...@>=
+typedef struct {
+ char *tfm_name; /* TFM file name */
+ char *ps_name; /* PostScript name */
+ integer flags; /* font flags */
+ char *ff_name; /* font file name */
+ char *subset_tag; /* pseudoUniqueTag for subsetted font */
+ enc_entry *encoding; /* pointer to corresponding encoding */
+ unsigned int tfm_num; /* number of the TFM refering this entry */
+ unsigned short type; /* font type (T1/TTF/...) */
+ short slant; /* SlantFont */
+ short extend; /* ExtendFont */
+ integer ff_objnum; /* FontFile object number */
+ integer fn_objnum; /* FontName/BaseName object number */
+ integer fd_objnum; /* FontDescriptor object number */
+ char *charset; /* string containing used glyphs */
+ boolean all_glyphs; /* embed all glyphs? */
+ unsigned short links; /* link flags from |tfm_tree| and |ps_tree| */
+ short tfm_avail; /* flags whether a tfm is available */
+ short pid; /* Pid for truetype fonts */
+ short eid; /* Eid for truetype fonts */
+} fm_entry;
+
+
+@
+@<Glob...@>=
+#define FONTNAME_BUF_SIZE 128
+boolean fontfile_found;
+boolean is_otf_font;
+char fontname_buf[FONTNAME_BUF_SIZE];
+
+@
+@d F_INCLUDED 0x01
+@d F_SUBSETTED 0x02
+@d F_TRUETYPE 0x04
+@d F_BASEFONT 0x08
+
+@d set_included(fm) ((fm)->type |= F_INCLUDED)
+@d set_subsetted(fm) ((fm)->type |= F_SUBSETTED)
+@d set_truetype(fm) ((fm)->type |= F_TRUETYPE)
+@d set_basefont(fm) ((fm)->type |= F_BASEFONT)
+
+@d is_included(fm) ((fm)->type & F_INCLUDED)
+@d is_subsetted(fm) ((fm)->type & F_SUBSETTED)
+@d is_truetype(fm) ((fm)->type & F_TRUETYPE)
+@d is_basefont(fm) ((fm)->type & F_BASEFONT)
+@d is_reencoded(fm) ((fm)->encoding != NULL)
+@d is_fontfile(fm) (fm_fontfile(fm) != NULL)
+@d is_t1fontfile(fm) (is_fontfile(fm) && !is_truetype(fm))
+
+@d fm_slant(fm) (fm)->slant
+@d fm_extend(fm) (fm)->extend
+@d fm_fontfile(fm) (fm)->ff_name
+
+@<Declarations@>=
+static boolean mp_font_is_reencoded (MP mp, font_number f);
+static boolean mp_font_is_included (MP mp, font_number f);
+static boolean mp_font_is_subsetted (MP mp, font_number f);
+
+@ @c
+boolean mp_font_is_reencoded (MP mp, font_number f) {
+ fm_entry *fm;
+ if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
+ if (fm != NULL
+ && (fm->ps_name != NULL)
+ && is_reencoded (fm))
+ return true;
+ }
+ return false;
+}
+boolean mp_font_is_included (MP mp, font_number f) {
+ fm_entry *fm;
+ if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f, &fm)) {
+ if (fm != NULL
+ && (fm->ps_name != NULL && fm->ff_name != NULL)
+ && is_included (fm))
+ return true;
+ }
+ return false;
+}
+boolean mp_font_is_subsetted (MP mp, font_number f) {
+ fm_entry *fm;
+ if (mp_has_font_size(mp,f) && mp_has_fm_entry (mp, f,&fm)) {
+ if (fm != NULL
+ && (fm->ps_name != NULL && fm->ff_name != NULL)
+ && is_included (fm) && is_subsetted (fm))
+ return true;
+ }
+ return false;
+}
+
+@ @<Declarations@>=
+static char * mp_fm_encoding_name (MP mp, font_number f);
+static char * mp_fm_font_name (MP mp, font_number f);
+static char * mp_fm_font_subset_name (MP mp, font_number f);
+
+@
+@c char * mp_fm_encoding_name (MP mp, font_number f) {
+ enc_entry *e;
+ fm_entry *fm;
+ if (mp_has_fm_entry (mp, f, &fm)) {
+ if (fm != NULL && (fm->ps_name != NULL)) {
+ if (is_reencoded (fm)) {
+ e = fm->encoding;
+ if (e->enc_name!=NULL)
+ return mp_xstrdup(mp,e->enc_name);
+ } else {
+ return NULL;
+ }
+ }
+ }
+ print_err ("fontmap encoding problems for font ");
+ mp_print(mp,mp->font_name[f]);
+ mp_error(mp);
+ return NULL;
+}
+char * mp_fm_font_name (MP mp, font_number f) {
+ fm_entry *fm;
+ if (mp_has_fm_entry (mp, f,&fm)) {
+ if (fm != NULL && (fm->ps_name != NULL)) {
+ if (mp_font_is_included(mp, f) && !mp->font_ps_name_fixed[f]) {
+ /* find the real fontname, and update |ps_name| and |subset_tag| if needed */
+ if (t1_updatefm(mp,f,fm)) {
+ mp->font_ps_name_fixed[f] = true;
+ } else {
+ print_err ("font loading problems for font ");
+ mp_print(mp,mp->font_name[f]);
+ mp_error(mp);
+ }
+ }
+ return mp_xstrdup(mp,fm->ps_name);
+ }
+ }
+ print_err ("fontmap name problems for font ");
+ mp_print(mp,mp->font_name[f]);
+ mp_error(mp);
+ return NULL;
+}
+
+static char * mp_fm_font_subset_name (MP mp, font_number f) {
+ fm_entry *fm;
+ if (mp_has_fm_entry (mp, f, &fm)) {
+ if (fm != NULL && (fm->ps_name != NULL)) {
+ if (is_subsetted(fm)) {
+ char *s = mp_xmalloc(mp,strlen(fm->ps_name)+8,1);
+ mp_snprintf(s,(int)strlen(fm->ps_name)+8,"%s-%s",fm->subset_tag,fm->ps_name);
+ return s;
+ } else {
+ return mp_xstrdup(mp,fm->ps_name);
+ }
+ }
+ }
+ print_err ("fontmap name problems for font ");
+ mp_print(mp,mp->font_name[f]);
+ mp_error(mp);
+ return NULL;
+}
+
+@ @<Declarations@>=
+static integer mp_fm_font_slant (MP mp, font_number f);
+static integer mp_fm_font_extend (MP mp, font_number f);
+
+@
+@c static integer mp_fm_font_slant (MP mp, font_number f) {
+ fm_entry *fm;
+ if (mp_has_fm_entry (mp, f, &fm)) {
+ if (fm != NULL && (fm->ps_name != NULL)) {
+ return fm->slant;
+ }
+ }
+ return 0;
+}
+static integer mp_fm_font_extend (MP mp, font_number f) {
+ fm_entry *fm;
+ if (mp_has_fm_entry (mp, f, &fm)) {
+ if (fm != NULL && (fm->ps_name != NULL)) {
+ return fm->extend;
+ }
+ }
+ return 0;
+}
+
+@ @<Declarations@>=
+static boolean mp_do_ps_font (MP mp, font_number f);
+
+@ @c static boolean mp_do_ps_font (MP mp, font_number f) {
+ fm_entry *fm_cur;
+ (void)mp_has_fm_entry (mp, f, &fm_cur); /* for side effects */
+ if (fm_cur == NULL)
+ return true;
+ if (is_truetype(fm_cur) ||
+ (fm_cur->ps_name == NULL && fm_cur->ff_name == NULL)) {
+ return false;
+ }
+ if (is_included(fm_cur)) {
+ mp_ps_print_nl(mp,"%%BeginResource: font ");
+ if (is_subsetted(fm_cur)) {
+ mp_ps_print(mp, fm_cur->subset_tag);
+ mp_ps_print_char(mp,'-');
+ }
+ mp_ps_print(mp, fm_cur->ps_name);
+ mp_ps_print_ln(mp);
+ writet1 (mp,f,fm_cur);
+ mp_ps_print_nl(mp,"%%EndResource");
+ mp_ps_print_ln(mp);
+ }
+ return true;
+}
+
+@ Included subset fonts do not need and encoding vector, make
+sure we skip that case.
+
+@<Declarations@>=
+static void mp_list_used_resources (MP mp, int prologues, int procset);
+
+@ @c static void mp_list_used_resources (MP mp, int prologues, int procset) {
+ font_number f; /* fonts used in a text node or as loop counters */
+ int ff; /* a loop counter */
+ int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
+ boolean firstitem;
+ if ( procset>0 )
+ mp_ps_print_nl(mp, "%%DocumentResources: procset mpost");
+ else
+ mp_ps_print_nl(mp, "%%DocumentResources: procset mpost-minimal");
+ ldf=null_font;
+ firstitem=true;
+ for (f=null_font+1;f<=mp->last_fnum;f++) {
+ if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
+ for (ff=ldf;ff>=null_font;ff--) {
+ if ( mp_has_font_size(mp,(font_number)ff) )
+ if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
+ goto FOUND;
+ }
+ if ( mp_font_is_subsetted(mp,f) )
+ goto FOUND;
+ if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>
+ (size_t)mp->max_print_line )
+ mp_ps_print_nl(mp, "%%+ encoding");
+ if ( firstitem ) {
+ firstitem=false;
+ mp_ps_print_nl(mp, "%%+ encoding");
+ }
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print(mp, mp->font_enc_name[f]);
+ ldf=(int)f;
+ }
+ FOUND:
+ ;
+ }
+ ldf=null_font;
+ firstitem=true;
+ for (f=null_font+1;f<=mp->last_fnum;f++) {
+ if ( mp_has_font_size(mp,f) ) {
+ for (ff=ldf;ff>=null_font;ff--) {
+ if ( mp_has_font_size(mp,(font_number)ff) )
+ if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
+ goto FOUND2;
+ }
+ if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>
+ (size_t)mp->max_print_line )
+ mp_ps_print_nl(mp, "%%+ font");
+ if ( firstitem ) {
+ firstitem=false;
+ mp_ps_print_nl(mp, "%%+ font");
+ }
+ mp_ps_print_char(mp, ' ');
+ if ( (prologues==3)&&
+ (mp_font_is_subsetted(mp,f)) )
+ mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
+ else
+ mp_ps_print(mp, mp->font_ps_name[f]);
+ ldf=(int)f;
+ }
+ FOUND2:
+ ;
+ }
+ mp_ps_print_ln(mp);
+}
+
+@ @<Declarations@>=
+static void mp_list_supplied_resources (MP mp, int prologues, int procset);
+
+@ @c static void mp_list_supplied_resources (MP mp, int prologues, int procset) {
+ font_number f; /* fonts used in a text node or as loop counters */
+ int ff; /* a loop counter */
+ int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
+ boolean firstitem;
+ if ( procset>0 )
+ mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost");
+ else
+ mp_ps_print_nl(mp, "%%DocumentSuppliedResources: procset mpost-minimal");
+ ldf=null_font;
+ firstitem=true;
+ for (f=null_font+1;f<=mp->last_fnum;f++) {
+ if ( (mp_has_font_size(mp,f))&&(mp_font_is_reencoded(mp,f)) ) {
+ for (ff=ldf;ff>= null_font;ff++) {
+ if ( mp_has_font_size(mp,(font_number)ff) )
+ if ( mp_xstrcmp(mp->font_enc_name[f],mp->font_enc_name[ff])==0 )
+ goto FOUND;
+ }
+ if ( (prologues==3)&&(mp_font_is_subsetted(mp,f)))
+ goto FOUND;
+ if ( mp->ps->ps_offset+1+strlen(mp->font_enc_name[f])>(size_t)mp->max_print_line )
+ mp_ps_print_nl(mp, "%%+ encoding");
+ if ( firstitem ) {
+ firstitem=false;
+ mp_ps_print_nl(mp, "%%+ encoding");
+ }
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print(mp, mp->font_enc_name[f]);
+ ldf=(int)f;
+ }
+ FOUND:
+ ;
+ }
+ ldf=null_font;
+ firstitem=true;
+ if (prologues==3) {
+ for (f=null_font+1;f<=mp->last_fnum;f++) {
+ if ( mp_has_font_size(mp,f) ) {
+ for (ff=ldf;ff>= null_font;ff--) {
+ if ( mp_has_font_size(mp,(font_number)ff) )
+ if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
+ goto FOUND2;
+ }
+ if ( ! mp_font_is_included(mp,f) )
+ goto FOUND2;
+ if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
+ mp_ps_print_nl(mp, "%%+ font");
+ if ( firstitem ) {
+ firstitem=false;
+ mp_ps_print_nl(mp, "%%+ font");
+ }
+ mp_ps_print_char(mp, ' ');
+ if ( mp_font_is_subsetted(mp,f) )
+ mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
+ else
+ mp_ps_print(mp, mp->font_ps_name[f]);
+ ldf=(int)f;
+ }
+ FOUND2:
+ ;
+ }
+ mp_ps_print_ln(mp);
+ }
+}
+
+@ @<Declarations...@>=
+static void mp_list_needed_resources (MP mp, int prologues);
+
+@ @c static void mp_list_needed_resources (MP mp, int prologues) {
+ font_number f; /* fonts used in a text node or as loop counters */
+ int ff; /* a loop counter */
+ int ldf; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
+ boolean firstitem;
+ ldf=null_font;
+ firstitem=true;
+ for (f=null_font+1;f<=mp->last_fnum;f++ ) {
+ if ( mp_has_font_size(mp,f)) {
+ for (ff=ldf;ff>=null_font;ff--) {
+ if ( mp_has_font_size(mp,(font_number)ff) )
+ if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
+ goto FOUND;
+ };
+ if ((prologues==3)&&(mp_font_is_included(mp,f)) )
+ goto FOUND;
+ if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
+ mp_ps_print_nl(mp, "%%+ font");
+ if ( firstitem ) {
+ firstitem=false;
+ mp_ps_print_nl(mp, "%%DocumentNeededResources: font");
+ }
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print(mp, mp->font_ps_name[f]);
+ ldf=(int)f;
+ }
+ FOUND:
+ ;
+ }
+ if ( ! firstitem ) {
+ mp_ps_print_ln(mp);
+ ldf=null_font;
+ firstitem=true;
+ for (f=null_font+1;f<= mp->last_fnum;f++) {
+ if ( mp_has_font_size(mp,f) ) {
+ for (ff=ldf;ff>=null_font;ff-- ) {
+ if ( mp_has_font_size(mp,(font_number)ff) )
+ if ( mp_xstrcmp(mp->font_name[f],mp->font_name[ff])==0 )
+ goto FOUND2;
+ }
+ if ((prologues==3)&&(mp_font_is_included(mp,f)) )
+ goto FOUND2;
+ mp_ps_print(mp, "%%IncludeResource: font ");
+ mp_ps_print(mp, mp->font_ps_name[f]);
+ mp_ps_print_ln(mp);
+ ldf=(int)f;
+ }
+ FOUND2:
+ ;
+ }
+ }
+}
+
+@ @<Declarations@>=
+static void mp_write_font_definition (MP mp, font_number f, int prologues);
+
+@
+
+@d applied_reencoding(A) ((mp_font_is_reencoded(mp,(A)))&&
+ ((! mp_font_is_subsetted(mp,(A)))||(prologues==2)))
+
+@c static void mp_write_font_definition(MP mp, font_number f, int prologues) {
+ if ( (applied_reencoding(f))||(mp_fm_font_slant(mp,f)!=0)||
+ (mp_fm_font_extend(mp,f)!=0)||
+ (mp_xstrcmp(mp->font_name[f],"psyrgo")==0)||
+ (mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0) ) {
+ if ( (mp_font_is_subsetted(mp,f))&&
+ (mp_font_is_included(mp,f))&&(prologues==3))
+ mp_ps_name_out(mp, mp_fm_font_subset_name(mp,f),true);
+ else
+ mp_ps_name_out(mp, mp->font_ps_name[f],true);
+ mp_ps_print(mp, " fcp");
+ mp_ps_print_ln(mp);
+ if ( applied_reencoding(f) ) {
+ mp_ps_print(mp, "/Encoding ");
+ mp_ps_print(mp, mp->font_enc_name[f]);
+ mp_ps_print(mp, " def ");
+ };
+ if ( mp_fm_font_slant(mp,f)!=0 ) {
+ mp_ps_print_int(mp, mp_fm_font_slant(mp,f));
+ mp_ps_print(mp, " SlantFont ");
+ };
+ if ( mp_fm_font_extend(mp,f)!=0 ) {
+ mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
+ mp_ps_print(mp, " ExtendFont ");
+ };
+ if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 ) {
+ mp_ps_print(mp, " 890 ScaleFont ");
+ mp_ps_print(mp, " 277 SlantFont ");
+ };
+ if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 ) {
+ mp_ps_print(mp, " FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
+ mp_ps_print(mp, "/Metrics 2 dict dup begin ");
+ mp_ps_print(mp, "/space[0 -278]def ");
+ mp_ps_print(mp, "/a12[-904 -939]def ");
+ mp_ps_print(mp, "end def ");
+ };
+ mp_ps_print(mp, "currentdict end");
+ mp_ps_print_ln(mp);
+ mp_ps_print_defined_name(mp,f,prologues);
+ mp_ps_print(mp, " exch definefont pop");
+ mp_ps_print_ln(mp);
+ }
+}
+
+@ @<Declarations@>=
+static void mp_ps_print_defined_name (MP mp, font_number f, int prologues);
+
+@
+@c static void mp_ps_print_defined_name(MP mp, font_number f, int prologues) {
+ mp_ps_print(mp, " /");
+ if ((mp_font_is_subsetted(mp,f))&&
+ (mp_font_is_included(mp,f))&&(prologues==3))
+ mp_ps_print(mp, mp_fm_font_subset_name(mp,f));
+ else
+ mp_ps_print(mp, mp->font_ps_name[f]);
+ if ( mp_xstrcmp(mp->font_name[f],"psyrgo")==0 )
+ mp_ps_print(mp, "-Slanted");
+ if ( mp_xstrcmp(mp->font_name[f],"zpzdr-reversed")==0 )
+ mp_ps_print(mp, "-Reverse");
+ if ( applied_reencoding(f) ) {
+ mp_ps_print(mp, "-");
+ mp_ps_print(mp, mp->font_enc_name[f]);
+ }
+ if ( mp_fm_font_slant(mp,f)!=0 ) {
+ mp_ps_print(mp, "-Slant_"); mp_ps_print_int(mp, mp_fm_font_slant(mp,f)) ;
+ }
+ if ( mp_fm_font_extend(mp,f)!=0 ) {
+ mp_ps_print(mp, "-Extend_"); mp_ps_print_int(mp, mp_fm_font_extend(mp,f));
+ }
+}
+
+@ @<Include encodings and fonts for edge structure~|h|@>=
+mp_font_encodings(mp,mp->last_fnum,(prologues==2));
+@<Embed fonts that are available@>
+
+@ @<Embed fonts that are available@>=
+{
+next_size=0;
+@<Make |cur_fsize| a copy of the |font_sizes| array@>;
+do {
+ done_fonts=true;
+ for (f=null_font+1;f<=mp->last_fnum;f++) {
+ if ( cur_fsize[f]!=null ) {
+ if (prologues==3 ) {
+ if ( ! mp_do_ps_font(mp,f) ) {
+ if ( mp_has_fm_entry(mp,f, NULL) ) {
+ print_err("Font embedding failed");
+ mp_error(mp);
+ }
+ }
+ }
+ cur_fsize[f]=link(cur_fsize[f]);
+ if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; }
+ }
+ }
+ if ( ! done_fonts )
+ @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
+ that size index@>;
+} while (! done_fonts);
+}
+
+@ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
+{
+ next_size++;
+ mp_apply_mark_string_chars(mp, h, next_size);
+}
+
+@ We also need to keep track of which characters are used in text nodes
+in the edge structure that is being shipped out. This is done by procedures
+that use the left-over |b3| field in the |char_info| words; i.e.,
+|char_info(f)(c).b3| gives the status of character |c| in font |f|.
+
+@<Types...@>=
+enum mp_char_mark_state {mp_unused=0, mp_used};
+
+@ @<Declarations@>=
+static void mp_mark_string_chars (MP mp,font_number f, char *s, size_t l) ;
+
+@ @c
+void mp_mark_string_chars (MP mp,font_number f, char *s, size_t l) {
+ integer b; /* |char_base[f]| */
+ int bc,ec; /* only characters between these bounds are marked */
+ char *k; /* an index into string |s| */
+ b=mp->char_base[f];
+ bc=(int)mp->font_bc[f];
+ ec=(int)mp->font_ec[f];
+ k=s;
+ while (l-->0){
+ if ( (*k>=bc)&&(*k<=ec) )
+ mp->font_info[b+*k].qqqq.b3=mp_used;
+ k++;
+ }
+}
+
+
+@ @<Declarations@>=
+static void mp_unmark_font (MP mp,font_number f) ;
+
+@ @c
+void mp_unmark_font (MP mp,font_number f) {
+ int k; /* an index into |font_info| */
+ for (k= mp->char_base[f]+mp->font_bc[f];
+ k<=mp->char_base[f]+mp->font_ec[f];
+ k++)
+ mp->font_info[k].qqqq.b3=mp_unused;
+}
+
+
+@ @<Declarations@>=
+static void mp_print_improved_prologue (MP mp, mp_edge_object *h, int p1, int procset) ;
+
+@ @c
+void mp_print_improved_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
+ quarterword next_size; /* the size index for fonts being listed */
+ pointer *cur_fsize; /* current positions in |font_sizes| */
+ boolean done_fonts; /* have we finished listing the fonts in the header? */
+ font_number f; /* a font number for loops */
+ cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer));
+ mp_list_used_resources(mp, prologues, procset);
+ mp_list_supplied_resources(mp, prologues, procset);
+ mp_list_needed_resources(mp, prologues);
+ mp_ps_print_nl(mp, "%%EndComments");
+ mp_ps_print_nl(mp, "%%BeginProlog");
+ if ( procset>0 )
+ mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
+ else
+ mp_ps_print_nl(mp, "%%BeginResource: procset mpost-minimal");
+ mp_ps_print_nl(mp, "/bd{bind def}bind def"
+ "/fshow {exch findfont exch scalefont setfont show}bd");
+ if ( procset>0 ) @<Print the procset@>;
+ mp_ps_print_nl(mp, "/fcp{findfont dup length dict begin"
+ "{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
+ mp_ps_print_nl(mp, "/fmc{FontMatrix dup length array copy dup dup}bd"
+ "/fmd{/FontMatrix exch def}bd");
+ mp_ps_print_nl(mp, "/Amul{4 -1 roll exch mul 1000 div}bd"
+ "/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
+ mp_ps_print_nl(mp, "/ScaleFont{dup fmc 0 get"
+ " Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
+ mp_ps_print_nl(mp, "/SlantFont{fmc 2 get dup 0 eq{pop 1}if"
+ " Amul FontMatrix 0 get mul 2 exch put fmd}bd");
+ mp_ps_print_nl(mp, "%%EndResource");
+ @<Include encodings and fonts for edge structure~|h|@>;
+ mp_ps_print_nl(mp, "%%EndProlog");
+ mp_ps_print_nl(mp, "%%BeginSetup");
+ mp_ps_print_ln(mp);
+ for (f=null_font+1;f<=mp->last_fnum;f++) {
+ if ( mp_has_font_size(mp,f) ) {
+ if ( mp_has_fm_entry(mp,f,NULL) ) {
+ mp_write_font_definition(mp,f, prologues);
+ mp_ps_name_out(mp, mp->font_name[f],true);
+ mp_ps_print_defined_name(mp,f, prologues);
+ mp_ps_print(mp, " def");
+ } else {
+ char s[256];
+ mp_snprintf(s,256,"font %s cannot be found in any fontmapfile!", mp->font_name[f]);
+ mp_warn(mp,s);
+ mp_ps_name_out(mp, mp->font_name[f],true);
+ mp_ps_name_out(mp, mp->font_name[f],true);
+ mp_ps_print(mp, " def");
+ }
+ mp_ps_print_ln(mp);
+ }
+ }
+ mp_ps_print_nl(mp, "%%EndSetup");
+ mp_ps_print_nl(mp, "%%Page: 1 1");
+ mp_ps_print_ln(mp);
+ mp_xfree(cur_fsize);
+}
+
+@ @<Declarations@>=
+static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues);
+
+
+@
+@c
+static font_number mp_print_font_comments (MP mp , mp_edge_object *h, int prologues) {
+ quarterword next_size; /* the size index for fonts being listed */
+ pointer *cur_fsize; /* current positions in |font_sizes| */
+ int ff; /* a loop counter */
+ boolean done_fonts; /* have we finished listing the fonts in the header? */
+ font_number f; /* a font number for loops */
+ scaled ds; /* design size and scale factor for a text node */
+ int ldf=0; /* the last \.{DocumentFont} listed (otherwise |null_font|) */
+ cur_fsize = mp_xmalloc(mp,(size_t)(mp->font_max+1),sizeof(pointer));
+ if ( prologues>0 ) {
+ @<Give a \.{DocumentFonts} comment listing all fonts with non-null
+ |font_sizes| and eliminate duplicates@>;
+ } else {
+ next_size=0;
+ @<Make |cur_fsize| a copy of the |font_sizes| array@>;
+ do { done_fonts=true;
+ for (f=null_font+1;f<=mp->last_fnum;f++) {
+ if ( cur_fsize[f]!=null ) {
+ @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
+ }
+ if ( cur_fsize[f]!=null ) { mp_unmark_font(mp, f); done_fonts=false; };
+ }
+ if ( ! done_fonts ) {
+ @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
+ that size index@>;
+ }
+ } while (! done_fonts);
+ }
+ mp_xfree(cur_fsize);
+ return (font_number)ldf;
+}
+
+@ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
+for (f=null_font+1;f<= mp->last_fnum;f++)
+ cur_fsize[f]=mp->font_sizes[f]
+
+@ It's not a good idea to make any assumptions about the |font_ps_name| entries,
+so we carefully remove duplicates. There is no harm in using a slow, brute-force
+search.
+
+@<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
+{
+ ldf=null_font;
+ for (f=null_font+1;f<= mp->last_fnum;f++) {
+ if ( mp->font_sizes[f]!=null ) {
+ if ( ldf==null_font )
+ mp_ps_print_nl(mp, "%%DocumentFonts:");
+ for (ff=ldf;ff>=null_font;ff--) {
+ if ( mp->font_sizes[ff]!=null )
+ if ( mp_xstrcmp(mp->font_ps_name[f],mp->font_ps_name[ff])==0 )
+ goto FOUND;
+ }
+ if ( mp->ps->ps_offset+1+strlen(mp->font_ps_name[f])>(size_t)mp->max_print_line )
+ mp_ps_print_nl(mp, "%%+");
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print(mp, mp->font_ps_name[f]);
+ ldf=(int)f;
+ FOUND:
+ ;
+ }
+ }
+}
+
+@ @c
+static void mp_hex_digit_out (MP mp,quarterword d) {
+ if ( d<10 ) mp_ps_print_char(mp, d+'0');
+ else mp_ps_print_char(mp, d+'a'-10);
+}
+
+@ We output the marks as a hexadecimal bit string starting at |c| or
+|font_bc[f]|, whichever is greater. If the output has to be truncated
+to avoid exceeding |emergency_line_length| the return value says where to
+start scanning next time.
+
+@<Declarations@>=
+static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c);
+
+@
+@d emergency_line_length 255
+ /* \ps\ output lines can be this long in unusual circumstances */
+
+@c
+static halfword mp_ps_marks_out (MP mp,font_number f, eight_bits c) {
+ eight_bits bc,ec; /* only encode characters between these bounds */
+ integer lim; /* the maximum number of marks to encode before truncating */
+ int p; /* |font_info| index for the current character */
+ int d; /* used to construct a hexadecimal digit */
+ unsigned b; /* used to construct a hexadecimal digit */
+ lim=4*(emergency_line_length-mp->ps->ps_offset-4);
+ bc=mp->font_bc[f];
+ ec=mp->font_ec[f];
+ if ( c>bc ) bc=c;
+ @<Restrict the range |bc..ec| so that it contains no unused characters
+ at either end and has length at most |lim|@>;
+ @<Print the initial label indicating that the bitmap starts at |bc|@>;
+ @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
+ while ( (ec<mp->font_ec[f])&&(mp->font_info[p].qqqq.b3==mp_unused) ) {
+ p++; ec++;
+ }
+ return (ec+1);
+}
+
+@ We could save time by setting the return value before the loop that
+decrements |ec|, but there is no point in being so tricky.
+
+@<Restrict the range |bc..ec| so that it contains no unused characters...@>=
+p=mp->char_base[f]+bc;
+while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
+ p++; bc++;
+}
+if ( ec>=(eight_bits)(bc+lim)) ec=(eight_bits)(bc+lim-1);
+p=mp->char_base[f]+ec;
+while ( (mp->font_info[p].qqqq.b3==mp_unused)&&(bc<ec) ) {
+ p--; ec--;
+}
+
+@ @<Print the initial label indicating that the bitmap starts at |bc|@>=
+mp_ps_print_char(mp, ' ');
+mp_hex_digit_out(mp, (quarterword)bc / 16);
+mp_hex_digit_out(mp, (quarterword)bc % 16);
+mp_ps_print_char(mp, ':')
+
+@
+
+@<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
+b=8; d=0;
+for (p=mp->char_base[f]+bc;p<=mp->char_base[f]+ec;p++) {
+ if ( b==0 ) {
+ mp_hex_digit_out(mp, (quarterword)d);
+ d=0; b=8;
+ }
+ if ( mp->font_info[p].qqqq.b3!=mp_unused ) d=d+b;
+ b=b>>1;
+}
+mp_hex_digit_out(mp, (quarterword)d)
+
+
+@ Here is a simple function that determines whether there are any marked
+characters in font~|f| with character code at least~|c|.
+
+@<Declarations@>=
+static boolean mp_check_ps_marks (MP mp,font_number f, integer c) ;
+
+@ @c
+static boolean mp_check_ps_marks (MP mp,font_number f, integer c) {
+ int p; /* |font_info| index for the current character */
+ for (p=mp->char_base[f]+c;p<=mp->char_base[f]+mp->font_ec[f];p++) {
+ if ( mp->font_info[p].qqqq.b3==mp_used )
+ return true;
+ }
+ return false;
+}
+
+
+@ If the file name is so long that it can't be printed without exceeding
+|emergency_line_length| then there will be missing items in the \.{\%*Font:}
+line. We might have to repeat line in order to get the character usage
+information to fit within |emergency_line_length|.
+
+TODO: these two defines are also defined in mp.w!
+
+@d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
+@d sc_factor(A) mp->mem[(A)+1].cint /* the scale factor stored in a font size node */
+
+@<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
+{ integer t=0;
+ while ( mp_check_ps_marks(mp, f,t) ) {
+ mp_ps_print_nl(mp, "%*Font: ");
+ if ( mp->ps->ps_offset+strlen(mp->font_name[f])+12>emergency_line_length )
+ break;
+ mp_ps_print(mp, mp->font_name[f]);
+ mp_ps_print_char(mp, ' ');
+ ds=(mp->font_dsize[f] + 8) / 16;
+ mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,sc_factor(cur_fsize[f])));
+ if ( mp->ps->ps_offset+12>emergency_line_length ) break;
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, ds);
+ if ( mp->ps->ps_offset+5>emergency_line_length ) break;
+ t=mp_ps_marks_out(mp, f, (eight_bits)t);
+ }
+ cur_fsize[f]=link(cur_fsize[f]);
+}
+
+@ @<Print the procset@>=
+{
+ mp_ps_print_nl(mp, "/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
+ mp_ps_print_nl(mp, "/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
+ mp_ps_print_nl(mp, "/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd"
+ "/p{closepath}bd/n{newpath}bd");
+ mp_ps_print_nl(mp, "/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd"
+ "/lj{setlinejoin}bd/ml{setmiterlimit}bd");
+ mp_ps_print_nl(mp, "/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd"
+ "/Q{grestore}bd/s{scale}bd/t{concat}bd");
+ mp_ps_print_nl(mp, "/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
+}
+
+
+@ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
+arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
+might not have reasonable |font_ps_name| entries, but we just charge ahead
+anyway. The user should not make \&{prologues} positive if this will cause
+trouble.
+@:prologues_}{\&{prologues} primitive@>
+
+@<Declarations@>=
+static void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset);
+
+@ @c
+void mp_print_prologue (MP mp, mp_edge_object *h, int prologues, int procset) {
+ font_number f;
+ font_number ldf ;
+ ldf = mp_print_font_comments (mp, h, prologues);
+ mp_ps_print_ln(mp);
+ if ( (prologues==1) && (mp->last_ps_fnum<mp->last_fnum) )
+ mp_read_psname_table(mp);
+ mp_ps_print(mp, "%%BeginProlog"); mp_ps_print_ln(mp);
+ if ( (prologues>0)||(procset>0) ) {
+ if ( ldf!=null_font ) {
+ if ( prologues>0 ) {
+ for (f=null_font+1;f<=mp->last_fnum;f++) {
+ if ( mp_has_font_size(mp,f) ) {
+ mp_ps_name_out(mp, mp->font_name[f],true);
+ mp_ps_name_out(mp, mp->font_ps_name[f],true);
+ mp_ps_print(mp, " def");
+ mp_ps_print_ln(mp);
+ }
+ }
+ if ( procset==0 ) {
+ mp_ps_print(mp, "/fshow {exch findfont exch scalefont setfont show}bind def");
+ mp_ps_print_ln(mp);
+ }
+ }
+ }
+ if (procset>0 ) {
+ mp_ps_print_nl(mp, "%%BeginResource: procset mpost");
+ if ( (prologues>0)&&(ldf!=null_font) )
+ mp_ps_print_nl(mp,
+ "/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
+ else
+ mp_ps_print_nl(mp, "/bd{bind def}bind def");
+ @<Print the procset@>;
+ mp_ps_print_nl(mp, "%%EndResource");
+ mp_ps_print_ln(mp);
+ }
+ }
+ mp_ps_print(mp, "%%EndProlog");
+ mp_ps_print_nl(mp, "%%Page: 1 1"); mp_ps_print_ln(mp);
+}
+
+@ \MP\ used to have one single routine to print to both `write' files
+and the PostScript output. Web2c redefines ``Character |k| cannot be
+printed'', and that resulted in some bugs where 8-bit characters were
+written to the PostScript file (reported by Wlodek Bzyl).
+
+Also, Hans Hagen requested spaces to be output as "\\040" instead of
+a plain space, since that makes it easier to parse the result file
+for postprocessing.
+
+@<Character |k| is not allowed in PostScript output@>=
+ (k<=' ')||(k>'~')
+
+@ We often need to print a pair of coordinates.
+
+@c
+void mp_ps_pair_out (MP mp,scaled x, scaled y) {
+ ps_room(26);
+ mp_ps_print_scaled(mp, x); mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, y); mp_ps_print_char(mp, ' ');
+}
+
+@ @<Declarations@>=
+static void mp_ps_pair_out (MP mp,scaled x, scaled y) ;
+
+@ @c
+void mp_ps_print_cmd (MP mp, const char *l, const char *s) {
+ if ( mp->internal[mp_procset]>0 ) { ps_room(strlen(s)); mp_ps_print(mp,s); }
+ else { ps_room(strlen(l)); mp_ps_print(mp, l); };
+}
+
+@ @<Declarations@>=
+static void mp_ps_print_cmd (MP mp, const char *l, const char *s) ;
+
+@ @c
+void mp_ps_string_out (MP mp, const char *s, size_t l) {
+ ASCII_code k; /* bits to be converted to octal */
+ mp_ps_print(mp, "(");
+ while (l-->0) {
+ k=(ASCII_code)*s++;
+ if ( mp->ps->ps_offset+5>mp->max_print_line ) {
+ mp_ps_print_char(mp, '\\');
+ mp_ps_print_ln(mp);
+ }
+ if ( (@<Character |k| is not allowed in PostScript output@>) ) {
+ mp_ps_print_char(mp, '\\');
+ mp_ps_print_char(mp, '0'+(k / 64));
+ mp_ps_print_char(mp, '0'+((k / 8) % 8));
+ mp_ps_print_char(mp, '0'+(k % 8));
+ } else {
+ if ( (k=='(')||(k==')')||(k=='\\') )
+ mp_ps_print_char(mp, '\\');
+ mp_ps_print_char(mp, k);
+ }
+ }
+ mp_ps_print_char(mp, ')');
+}
+
+@ @<Declarations@>=
+static void mp_ps_string_out (MP mp, const char *s, size_t l) ;
+
+@ This is a define because the function does not use its |mp| argument.
+
+@d mp_is_ps_name(M,A) mp_do_is_ps_name(A)
+
+@c
+static boolean mp_do_is_ps_name (char *s) {
+ ASCII_code k; /* the character being checked */
+ while ((k=(ASCII_code)*s++)) {
+ if ( (k<=' ')||(k>'~') ) return false;
+ if ( (k=='(')||(k==')')||(k=='<')||(k=='>')||
+ (k=='{')||(k=='}')||(k=='/')||(k=='%') ) return false;
+ }
+ return true;
+}
+
+@ @<Declarations@>=
+static void mp_ps_name_out (MP mp, char *s, boolean lit) ;
+
+@ @c
+void mp_ps_name_out (MP mp, char *s, boolean lit) {
+ ps_room(strlen(s)+2);
+ mp_ps_print_char(mp, ' ');
+ if ( mp_is_ps_name(mp, s) ) {
+ if ( lit ) mp_ps_print_char(mp, '/');
+ mp_ps_print(mp, s);
+ } else {
+ mp_ps_string_out(mp, s, strlen(s));
+ if ( ! lit ) mp_ps_print(mp, "cvx ");
+ mp_ps_print(mp, "cvn");
+ }
+}
+
+
+@ These special comments described in the {\sl PostScript Language Reference
+Manual}, 2nd.~edition are understood by some \ps-reading programs.
+We can't normally output ``conforming'' \ps\ because
+the structuring conventions don't allow us to say ``Please make sure the
+following characters are downloaded and define the \.{fshow} macro to access
+them.''
+
+The exact bounding box is written out if |mp_prologues<0|, although this
+is not standard \ps, since it allows \TeX\ to calculate the box dimensions
+accurately. (Overfull boxes are avoided if an illustration is made to
+match a given \.{\char`\\hsize}.)
+
+@<Declarations@>=
+static void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues);
+
+@ @c
+void mp_print_initial_comment(MP mp,mp_edge_object *hh, int prologues) {
+ scaled t;
+ char *s;
+ mp_ps_print(mp, "%!PS");
+ if ( prologues>0 )
+ mp_ps_print(mp, "-Adobe-3.0 EPSF-3.0");
+ mp_ps_print_nl(mp, "%%BoundingBox: ");
+ if ( hh->minx>hh->maxx) {
+ mp_ps_print(mp, "0 0 0 0");
+ } else if ( prologues<0 ) {
+ mp_ps_pair_out(mp, hh->minx,hh->miny);
+ mp_ps_pair_out(mp, hh->maxx,hh->maxy);
+ } else {
+ mp_ps_pair_out(mp, mp_floor_scaled(mp, hh->minx),mp_floor_scaled(mp, hh->miny));
+ mp_ps_pair_out(mp, -mp_floor_scaled(mp, -hh->maxx),-mp_floor_scaled(mp, -hh->maxy));
+ }
+ mp_ps_print_nl(mp, "%%HiResBoundingBox: ");
+ if ( hh->minx>hh->maxx ) {
+ mp_ps_print(mp, "0 0 0 0");
+ } else {
+ mp_ps_pair_out(mp, hh->minx,hh->miny);
+ mp_ps_pair_out(mp, hh->maxx,hh->maxy);
+ }
+ mp_ps_print_nl(mp, "%%Creator: MetaPost ");
+ s = mp_metapost_version();
+ mp_ps_print(mp, s);
+ mp_xfree(s);
+ mp_ps_print_nl(mp, "%%CreationDate: ");
+ mp_ps_print_int(mp, mp_round_unscaled(mp, mp->internal[mp_year]));
+ mp_ps_print_char(mp, '.');
+ mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_month]));
+ mp_ps_print_char(mp, '.');
+ mp_ps_print_dd(mp, mp_round_unscaled(mp, mp->internal[mp_day]));
+ mp_ps_print_char(mp, ':');
+ t=mp_round_unscaled(mp, mp->internal[mp_time]);
+ mp_ps_print_dd(mp, t / 60);
+ mp_ps_print_dd(mp, t % 60);
+ mp_ps_print_nl(mp, "%%Pages: 1");
+}
+
+@ The most important output procedure is the one that gives the \ps\ version of
+a \MP\ path.
+
+@(mplibps.h@>=
+typedef struct mp_knot {
+ unsigned short left_type;
+ unsigned short right_type;
+ signed int x_coord;
+ signed int y_coord;
+ signed int left_x;
+ signed int left_y;
+ signed int right_x;
+ signed int right_y;
+ struct mp_knot * next;
+ unsigned char originator;
+} mp_knot;
+
+@ @<Types...@>=
+#define gr_left_type(A) (A)->left_type
+#define gr_right_type(A) (A)->right_type
+#define gr_x_coord(A) (A)->x_coord
+#define gr_y_coord(A) (A)->y_coord
+#define gr_left_x(A) (A)->left_x
+#define gr_left_y(A) (A)->left_y
+#define gr_right_x(A) (A)->right_x
+#define gr_right_y(A) (A)->right_y
+#define gr_next_knot(A) (A)->next
+#define gr_originator(A) (A)->originator
+
+@ If we want to duplicate a knot node, we can say |copy_knot|:
+
+@c
+static mp_knot *mp_gr_copy_knot (MP mp, mp_knot *p) {
+ mp_knot *q; /* the copy */
+ q = mp_xmalloc(mp, 1, sizeof (mp_knot));
+ memcpy(q,p,sizeof (mp_knot));
+ gr_next_knot(q)=NULL;
+ return q;
+}
+
+@ The |copy_path| routine makes a clone of a given path.
+
+@c
+static mp_knot *mp_gr_copy_path (MP mp, mp_knot *p) {
+ mp_knot *q, *pp, *qq; /* for list manipulation */
+ if (p==NULL)
+ return NULL;
+ q=mp_gr_copy_knot(mp, p);
+ qq=q;
+ pp=gr_next_knot(p);
+ while ( pp!=p ) {
+ gr_next_knot(qq)=mp_gr_copy_knot(mp, pp);
+ qq=gr_next_knot(qq);
+ pp=gr_next_knot(pp);
+ }
+ gr_next_knot(qq)=q;
+ return q;
+}
+
+@ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
+returns a pointer to the first node of the copy, if the path is a cycle,
+but to the final node of a non-cyclic copy. The global
+variable |path_tail| will point to the final node of the original path;
+this trick makes it easier to implement `\&{doublepath}'.
+
+All node types are assumed to be |endpoint| or |explicit| only.
+
+This function is currenly unused.
+
+@c
+mp_knot * mp_gr_htap_ypoc (MP mp, mp_knot *p) {
+ mp_knot *q, *pp, *qq, *rr; /* for list manipulation */
+ q=mp_xmalloc(mp, 1, sizeof (mp_knot)); /* this will correspond to |p| */
+ qq=q; pp=p;
+ while (1) {
+ gr_right_type(qq)=gr_left_type(pp);
+ gr_left_type(qq)=gr_right_type(pp);
+ gr_x_coord(qq)=gr_x_coord(pp);
+ gr_y_coord(qq)=gr_y_coord(pp);
+ gr_right_x(qq)=gr_left_x(pp);
+ gr_right_y(qq)=gr_left_y(pp);
+ gr_left_x(qq)=gr_right_x(pp);
+ gr_left_y(qq)=gr_right_y(pp);
+ gr_originator(qq)=gr_originator(pp);
+ if ( gr_next_knot(pp)==p ) {
+ gr_next_knot(q)=qq;
+ /* |mp->path_tail=pp;| */ /* ? */
+ return q;
+ }
+ rr=mp_xmalloc(mp, 1, sizeof (mp_knot));
+ gr_next_knot(rr)=qq;
+ qq=rr;
+ pp=gr_next_knot(pp);
+ }
+}
+
+@ When a cyclic list of knot nodes is no longer needed, it can be recycled by
+calling the following subroutine.
+
+@<Declarations@>=
+static void mp_do_gr_toss_knot_list (mp_knot *p) ;
+
+@
+@d mp_gr_toss_knot_list(B,A) mp_do_gr_toss_knot_list(A)
+
+@c
+void mp_do_gr_toss_knot_list (mp_knot * p) {
+ mp_knot *q; /* the node being freed */
+ mp_knot *r; /* the next node */
+ if (p==NULL)
+ return;
+ q=p;
+ do {
+ r=gr_next_knot(q);
+ mp_xfree(q); q=r;
+ } while (q!=p);
+}
+
+
+
+@ @c
+static void mp_gr_ps_path_out (MP mp, mp_knot *h) {
+ mp_knot *p, *q; /* for scanning the path */
+ scaled d; /* a temporary value */
+ boolean curved; /* |true| unless the cubic is almost straight */
+ ps_room(40);
+ mp_ps_print_cmd(mp, "newpath ","n ");
+ mp_ps_pair_out(mp, gr_x_coord(h),gr_y_coord(h));
+ mp_ps_print_cmd(mp, "moveto","m");
+ p=h;
+ do {
+ if ( gr_right_type(p)==mp_endpoint ) {
+ if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
+ return;
+ }
+ q=gr_next_knot(p);
+ @<Start a new line and print the \ps\ commands for the curve from
+ |p| to~|q|@>;
+ p=q;
+ } while (p!=h);
+ mp_ps_print_cmd(mp, " closepath"," p");
+}
+
+@ @<Start a new line and print the \ps\ commands for the curve from...@>=
+curved=true;
+@<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
+mp_ps_print_ln(mp);
+if ( curved ){
+ mp_ps_pair_out(mp, gr_right_x(p),gr_right_y(p));
+ mp_ps_pair_out(mp, gr_left_x(q),gr_left_y(q));
+ mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
+ mp_ps_print_cmd(mp, "curveto","c");
+} else if ( q!=h ){
+ mp_ps_pair_out(mp, gr_x_coord(q),gr_y_coord(q));
+ mp_ps_print_cmd(mp, "lineto","l");
+}
+
+@ Two types of straight lines come up often in \MP\ paths:
+cubics with zero initial and final velocity as created by |make_path| or
+|make_envelope|, and cubics with control points uniformly spaced on a line
+as created by |make_choices|.
+
+@d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
+
+@<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
+if ( gr_right_x(p)==gr_x_coord(p) )
+ if ( gr_right_y(p)==gr_y_coord(p) )
+ if ( gr_left_x(q)==gr_x_coord(q) )
+ if ( gr_left_y(q)==gr_y_coord(q) ) curved=false;
+d=gr_left_x(q)-gr_right_x(p);
+if ( abs(gr_right_x(p)-gr_x_coord(p)-d)<=bend_tolerance )
+ if ( abs(gr_x_coord(q)-gr_left_x(q)-d)<=bend_tolerance )
+ { d=gr_left_y(q)-gr_right_y(p);
+ if ( abs(gr_right_y(p)-gr_y_coord(p)-d)<=bend_tolerance )
+ if ( abs(gr_y_coord(q)-gr_left_y(q)-d)<=bend_tolerance ) curved=false;
+ }
+
+@ The colored objects use a struct with anonymous fields to express the color parts:
+
+@(mplibps.h@>=
+typedef struct {
+ int a_val, b_val, c_val, d_val;
+} mp_color;
+
+@ The exported form of a dash pattern is simpler than the internal
+format, it is closely modelled to the PostScript model. The array of
+dashes is ended by a single negative value, because this is not
+allowed in PostScript.
+
+@(mplibps.h@>=
+typedef struct {
+ int offset;
+ int *array;
+} mp_dash_object ;
+
+
+@
+@d mp_gr_toss_dashes(A,B) mp_do_gr_toss_dashes(B)
+
+@<Declarations@>=
+static void mp_do_gr_toss_dashes(mp_dash_object *dl);
+
+@ @c
+void mp_do_gr_toss_dashes(mp_dash_object *dl) {
+ if (dl==NULL)
+ return;
+ mp_xfree(dl->array);
+ mp_xfree(dl);
+}
+
+
+@ @c
+static mp_dash_object *mp_gr_copy_dashes(MP mp, mp_dash_object *dl) {
+ mp_dash_object *q = NULL;
+ (void)mp;
+ if (dl==NULL)
+ return NULL;
+ q = mp_xmalloc(mp, 1, sizeof (mp_dash_object));
+ memcpy (q,dl,sizeof(mp_dash_object));
+ if (dl->array != NULL) {
+ size_t i = 0;
+ while (*(dl->array+i) != -1) i++;
+ q->array = mp_xmalloc(mp, i, sizeof (scaled));
+ memcpy(q->array,dl->array, (i*sizeof(scaled)));
+ }
+ return q;
+}
+
+
+@ Now for outputting the actual graphic objects. First, set up some
+structures and access macros.
+
+@d gr_has_color(A) (gr_type((A))<mp_start_clip_code)
+
+@<Types...@>=
+#define gr_type(A) (A)->type
+#define gr_link(A) (A)->next
+#define gr_color_model(A) (A)->color_model
+#define gr_red_val(A) (A)->color.a_val
+#define gr_green_val(A) (A)->color.b_val
+#define gr_blue_val(A) (A)->color.c_val
+#define gr_cyan_val(A) (A)->color.a_val
+#define gr_magenta_val(A) (A)->color.b_val
+#define gr_yellow_val(A) (A)->color.c_val
+#define gr_black_val(A) (A)->color.d_val
+#define gr_grey_val(A) (A)->color.a_val
+#define gr_path_p(A) (A)->path_p
+#define gr_htap_p(A) ((mp_fill_object *)A)->htap_p
+#define gr_pen_p(A) (A)->pen_p
+#define gr_ljoin_val(A) (A)->ljoin
+#define gr_lcap_val(A) ((mp_stroked_object *)A)->lcap
+#define gr_miterlim_val(A) (A)->miterlim
+#define gr_pre_script(A) (A)->pre_script
+#define gr_post_script(A) (A)->post_script
+#define gr_dash_p(A) ((mp_stroked_object *)A)->dash_p
+#define gr_size_index(A) ((mp_text_object *)A)->size_index
+#define gr_text_p(A) ((mp_text_object *)A)->text_p
+#define gr_text_l(A) ((mp_text_object *)A)->text_l
+#define gr_font_n(A) ((mp_text_object *)A)->font_n
+#define gr_font_name(A) ((mp_text_object *)A)->font_name
+#define gr_font_dsize(A) ((mp_text_object *)A)->font_dsize
+#define gr_width_val(A) ((mp_text_object *)A)->width
+#define gr_height_val(A) ((mp_text_object *)A)->height
+#define gr_depth_val(A) ((mp_text_object *)A)->depth
+#define gr_tx_val(A) ((mp_text_object *)A)->tx
+#define gr_ty_val(A) ((mp_text_object *)A)->ty
+#define gr_txx_val(A) ((mp_text_object *)A)->txx
+#define gr_txy_val(A) ((mp_text_object *)A)->txy
+#define gr_tyx_val(A) ((mp_text_object *)A)->tyx
+#define gr_tyy_val(A) ((mp_text_object *)A)->tyy
+
+@ @(mplibps.h@>=
+#define GRAPHIC_BODY \
+ int type; \
+ struct mp_graphic_object * next
+
+typedef struct mp_graphic_object {
+ GRAPHIC_BODY;
+} mp_graphic_object;
+
+typedef struct mp_text_object {
+ GRAPHIC_BODY;
+ char *pre_script;
+ char *post_script;
+ mp_color color;
+ unsigned char color_model;
+ unsigned char size_index;
+ char *text_p;
+ size_t text_l;
+ char *font_name ;
+ unsigned int font_dsize ;
+ unsigned int font_n ;
+ int width ;
+ int height ;
+ int depth ;
+ int tx ;
+ int ty ;
+ int txx ;
+ int txy ;
+ int tyx ;
+ int tyy ;
+} mp_text_object;
+
+typedef struct mp_fill_object {
+ GRAPHIC_BODY;
+ char *pre_script;
+ char *post_script;
+ mp_color color;
+ unsigned char color_model;
+ unsigned char ljoin ;
+ mp_knot * path_p;
+ mp_knot * htap_p;
+ mp_knot * pen_p;
+ int miterlim ;
+} mp_fill_object;
+
+typedef struct mp_stroked_object {
+ GRAPHIC_BODY;
+ char *pre_script;
+ char *post_script;
+ mp_color color;
+ unsigned char color_model;
+ unsigned char ljoin ;
+ unsigned char lcap ;
+ mp_knot * path_p;
+ mp_knot * pen_p;
+ int miterlim ;
+ mp_dash_object *dash_p;
+} mp_stroked_object;
+
+typedef struct mp_clip_object {
+ GRAPHIC_BODY;
+ mp_knot * path_p;
+} mp_clip_object;
+
+typedef struct mp_bounds_object {
+ GRAPHIC_BODY;
+ mp_knot * path_p;
+} mp_bounds_object;
+
+typedef struct mp_special_object {
+ GRAPHIC_BODY;
+ char *pre_script;
+} mp_special_object ;
+
+typedef struct mp_edge_object {
+ struct mp_graphic_object * body;
+ struct mp_edge_object * next;
+ char * filename;
+ MP parent;
+ int minx, miny, maxx, maxy;
+ int width, height, depth, ital_corr;
+ int charcode;
+} mp_edge_object;
+
+@ @<Exported function headers@>=
+mp_graphic_object *mp_new_graphic_object(MP mp, int type);
+
+@ @c
+mp_graphic_object *mp_new_graphic_object (MP mp, int type) {
+ mp_graphic_object *p;
+ size_t size ;
+ switch (type) {
+ case mp_fill_code: size = sizeof(mp_fill_object); break;
+ case mp_stroked_code: size = sizeof(mp_stroked_object); break;
+ case mp_text_code: size = sizeof(mp_text_object); break;
+ case mp_start_clip_code: size = sizeof(mp_clip_object); break;
+ case mp_start_bounds_code: size = sizeof(mp_bounds_object); break;
+ case mp_special_code: size = sizeof(mp_special_object); break;
+ default: size = sizeof(mp_graphic_object); break;
+ }
+ p = (mp_graphic_object *)mp_xmalloc(mp,1,size);
+ memset(p,0,size);
+ gr_type(p) = type;
+ return p;
+}
+
+@ We need to keep track of several parameters from the \ps\ graphics state.
+@^graphics state@>
+This allows us to be sure that \ps\ has the correct values when they are
+needed without wasting time and space setting them unnecessarily.
+
+@d gs_red mp->ps->gs_state->red_field
+@d gs_green mp->ps->gs_state->green_field
+@d gs_blue mp->ps->gs_state->blue_field
+@d gs_black mp->ps->gs_state->black_field
+@d gs_colormodel mp->ps->gs_state->colormodel_field
+@d gs_ljoin mp->ps->gs_state->ljoin_field
+@d gs_lcap mp->ps->gs_state->lcap_field
+@d gs_adj_wx mp->ps->gs_state->adj_wx_field
+@d gs_miterlim mp->ps->gs_state->miterlim_field
+@d gs_dash_p mp->ps->gs_state->dash_p_field
+@d gs_dash_init_done mp->ps->gs_state->dash_done_field
+@d gs_previous mp->ps->gs_state->previous_field
+@d gs_width mp->ps->gs_state->width_field
+
+@<Types...@>=
+typedef struct _gs_state {
+ scaled red_field ;
+ scaled green_field ;
+ scaled blue_field ;
+ scaled black_field ;
+ /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
+ quarterword colormodel_field ;
+ /* the current colormodel */
+ quarterword ljoin_field ;
+ quarterword lcap_field ;
+ /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
+ quarterword adj_wx_field ;
+ /* what resolution-dependent adjustment applies to the width */
+ scaled miterlim_field ;
+ /* the value from the last \&{setmiterlimit} command */
+ mp_dash_object * dash_p_field ;
+ /* edge structure for last \&{setdash} command */
+ boolean dash_done_field ; /* to test for initial \&{setdash} */
+ struct _gs_state * previous_field ;
+ /* backlink to the previous |_gs_state| structure */
+ scaled width_field ;
+ /* width setting or $-1$ if no \&{setlinewidth} command so far */
+} _gs_state;
+
+
+@ @<Glob...@>=
+struct _gs_state * gs_state;
+
+@ @<Set init...@>=
+mp->ps->gs_state=NULL;
+
+@ @<Dealloc variables@>=
+mp_xfree(mp->ps->gs_state);
+
+@ To avoid making undue assumptions about the initial graphics state, these
+parameters are given special values that are guaranteed not to match anything
+in the edge structure being shipped out. On the other hand, the initial color
+should be black so that the translation of an all-black picture will have no
+\&{setcolor} commands. (These would be undesirable in a font application.)
+Hence we use |c=0| when initializing the graphics state and we use |c<0|
+to recover from a situation where we have lost track of the graphics state.
+
+@d mp_void (null+1) /* a null pointer different from |null| */
+
+@c static void mp_gs_unknown_graphics_state (MP mp,scaled c) {
+ struct _gs_state *p; /* to shift graphic states around */
+ if ( (c==0)||(c==-1) ) {
+ if ( mp->ps->gs_state==NULL ) {
+ mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
+ gs_previous=NULL;
+ } else {
+ while ( gs_previous!=NULL ) {
+ p = gs_previous;
+ mp_xfree(mp->ps->gs_state);
+ mp->ps->gs_state=p;
+ }
+ }
+ gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
+ gs_colormodel=mp_uninitialized_model;
+ gs_ljoin=3;
+ gs_lcap=3;
+ gs_miterlim=0;
+ gs_dash_p=NULL;
+ gs_dash_init_done=false;
+ gs_width=-1;
+ } else if ( c==1 ) {
+ p= mp->ps->gs_state;
+ mp->ps->gs_state = mp_xmalloc(mp,1,sizeof(struct _gs_state));
+ memcpy(mp->ps->gs_state,p,sizeof(struct _gs_state));
+ gs_previous = p;
+ } else if ( c==2 ) {
+ p = gs_previous;
+ mp_xfree(mp->ps->gs_state);
+ mp->ps->gs_state=p;
+ }
+}
+
+
+@ When it is time to output a graphical object, |fix_graphics_state| ensures
+that \ps's idea of the graphics state agrees with what is stored in the object.
+
+@<Declarations@>=
+static void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) ;
+
+@ @c
+void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) {
+ /* get ready to output graphical object |p| */
+ mp_knot *pp, *path_p; /* for list manipulation */
+ mp_dash_object *hh;
+ scaled wx,wy,ww; /* dimensions of pen bounding box */
+ quarterword adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
+ integer tx,ty; /* temporaries for computing |adj_wx| */
+ if ( gr_has_color(p) )
+ @<Make sure \ps\ will use the right color for object~|p|@>;
+ if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) {
+ if (gr_type(p)==mp_fill_code) {
+ pp = gr_pen_p((mp_fill_object *)p);
+ path_p = gr_path_p((mp_fill_object *)p);
+ } else {
+ pp = gr_pen_p((mp_stroked_object *)p);
+ path_p = gr_path_p((mp_stroked_object *)p);
+ }
+ if ( pp!=NULL )
+ if ( pen_is_elliptical(pp) ) {
+ @<Generate \ps\ code that sets the stroke width to the
+ appropriate rounded value@>;
+ @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
+ @<Decide whether the line cap parameter matters and set it if necessary@>;
+ @<Set the other numeric parameters as needed for object~|p|@>;
+ }
+ }
+ if ( mp->ps->ps_offset>0 ) mp_ps_print_ln(mp);
+}
+
+@ @<Decide whether the line cap parameter matters and set it if necessary@>=
+if ( gr_type(p)==mp_stroked_code ) {
+ mp_stroked_object *ts = (mp_stroked_object *)p;
+ if ( (gr_left_type(gr_path_p(ts))==mp_endpoint)||(gr_dash_p(ts)!=NULL) )
+ if ( gs_lcap!=(quarterword)gr_lcap_val(ts) ) {
+ ps_room(13);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_char(mp, '0'+gr_lcap_val(ts));
+ mp_ps_print_cmd(mp, " setlinecap"," lc");
+ gs_lcap=(quarterword)gr_lcap_val(ts);
+ }
+}
+
+@
+@d set_ljoin_miterlim(p)
+ if ( gs_ljoin!=(quarterword)gr_ljoin_val(p) ) {
+ ps_room(14);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_char(mp, '0'+gr_ljoin_val(p));
+ mp_ps_print_cmd(mp, " setlinejoin"," lj");
+ gs_ljoin=(quarterword)gr_ljoin_val(p);
+ }
+ if ( gs_miterlim!=gr_miterlim_val(p) ) {
+ ps_room(27);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, gr_miterlim_val(p));
+ mp_ps_print_cmd(mp, " setmiterlimit"," ml");
+ gs_miterlim=gr_miterlim_val(p);
+ }
+
+@<Set the other numeric parameters as needed for object~|p|@>=
+if ( gr_type(p)==mp_stroked_code ) {
+ mp_stroked_object *ts = (mp_stroked_object *)p;
+ set_ljoin_miterlim(ts);
+} else {
+ mp_fill_object *ts = (mp_fill_object *)p;
+ set_ljoin_miterlim(ts);
+}
+
+@
+@d set_color_objects(pq)
+ object_color_model = pq->color_model;
+ object_color_a = pq->color.a_val;
+ object_color_b = pq->color.b_val;
+ object_color_c = pq->color.c_val;
+ object_color_d = pq->color.d_val;
+
+@<Make sure \ps\ will use the right color for object~|p|@>=
+{
+ int object_color_model;
+ int object_color_a, object_color_b, object_color_c, object_color_d ;
+ if (gr_type(p) == mp_fill_code) {
+ mp_fill_object *pq = (mp_fill_object *)p;
+ set_color_objects(pq);
+ } else if (gr_type(p) == mp_stroked_code) {
+ mp_stroked_object *pq = (mp_stroked_object *)p;
+ set_color_objects(pq);
+ } else {
+ mp_text_object *pq = (mp_text_object *)p;
+ set_color_objects(pq);
+ }
+
+ if ( object_color_model==mp_rgb_model) {
+ if ( (gs_colormodel!=mp_rgb_model)||(gs_red!=object_color_a)||
+ (gs_green!=object_color_b)||(gs_blue!=object_color_c) ) {
+ gs_red = object_color_a;
+ gs_green = object_color_b;
+ gs_blue = object_color_c;
+ gs_black = -1;
+ gs_colormodel=mp_rgb_model;
+ { ps_room(36);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, gs_red); mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, gs_green); mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, gs_blue);
+ mp_ps_print_cmd(mp, " setrgbcolor", " R");
+ }
+ }
+ } else if ( object_color_model==mp_cmyk_model) {
+ if ( (gs_red!=object_color_a)||(gs_green!=object_color_b)||
+ (gs_blue!=object_color_c)||(gs_black!=object_color_d)||
+ (gs_colormodel!=mp_cmyk_model) ) {
+ gs_red = object_color_a;
+ gs_green = object_color_b;
+ gs_blue = object_color_c;
+ gs_black = object_color_d;
+ gs_colormodel=mp_cmyk_model;
+ { ps_room(45);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, gs_red);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, gs_green);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, gs_blue);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, gs_black);
+ mp_ps_print_cmd(mp, " setcmykcolor"," C");
+ }
+ }
+ } else if ( object_color_model==mp_grey_model ) {
+ if ( (gs_red!=object_color_a)||(gs_colormodel!=mp_grey_model) ) {
+ gs_red = object_color_a;
+ gs_green = -1;
+ gs_blue = -1;
+ gs_black = -1;
+ gs_colormodel=mp_grey_model;
+ { ps_room(16);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, gs_red);
+ mp_ps_print_cmd(mp, " setgray"," G");
+ }
+ }
+ } else if ( object_color_model==mp_no_model ) {
+ gs_colormodel=mp_no_model;
+ }
+}
+
+@ In order to get consistent widths for horizontal and vertical pen strokes, we
+want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
+@:setwidth}{\&{setwidth}command@>
+We set |gs_width| to the ideal horizontal or vertical stroke width and then
+generate \ps\ code that computes the rounded value. For non-circular pens, the
+pen shape will be rescaled so that horizontal or vertical parts of the stroke
+have the computed width.
+
+Rounding the width to whole pixels is not likely to improve the appearance of
+diagonal or curved strokes, but we do it anyway for consistency. The
+\&{truncate} command generated here tends to make all the strokes a little
+@:truncate}{\&{truncate} command@>
+thinner, but this is appropriate for \ps's scan-conversion rules. Even with
+truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
+It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
+to compute in \ps.
+
+@<Generate \ps\ code that sets the stroke width...@>=
+@<Set |wx| and |wy| to the width and height of the bounding box for
+ |pen_p(p)|@>;
+@<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
+ important and set |adj_wx| and |ww| accordingly@>;
+if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
+ if ( adj_wx != 0 ) {
+ ps_room(13);
+ mp_ps_print_char(mp, ' '); mp_ps_print_scaled(mp, ww);
+ mp_ps_print_cmd(mp,
+ " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
+ } else {
+ if ( mp->internal[mp_procset]>0 ) {
+ ps_room(13);
+ mp_ps_print_char(mp, ' ');
+ mp_ps_print_scaled(mp, ww);
+ mp_ps_print(mp, " vlw");
+ } else {
+ ps_room(15);
+ mp_ps_print(mp, " 0 "); mp_ps_print_scaled(mp, ww);
+ mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
+ }
+ }
+ gs_width = ww;
+ gs_adj_wx = adj_wx;
+}
+
+@ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
+if ( (gr_right_x(pp)==gr_x_coord(pp)) && (gr_left_y(pp)==gr_y_coord(pp)) ) {
+ wx = abs(gr_left_x(pp) - gr_x_coord(pp));
+ wy = abs(gr_right_y(pp) - gr_y_coord(pp));
+} else {
+ wx = mp_pyth_add(mp, gr_left_x(pp)-gr_x_coord(pp), gr_right_x(pp)-gr_x_coord(pp));
+ wy = mp_pyth_add(mp, gr_left_y(pp)-gr_y_coord(pp), gr_right_y(pp)-gr_y_coord(pp));
+}
+
+@ The path is considered ``essentially horizontal'' if its range of
+$y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
+vertical'' paths are detected similarly. This code ensures that no component
+of the pen transformation is more that |aspect_bound*(ww+1)|.
+
+@d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
+ more than this factor */
+
+@d do_x_loc 1
+@d do_y_loc 2
+
+@<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
+tx=1; ty=1;
+if ( mp_gr_coord_rangeOK(path_p, do_y_loc, wy) ) tx=aspect_bound;
+else if ( mp_gr_coord_rangeOK(path_p, do_x_loc, wx) ) ty=aspect_bound;
+if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=0; }
+else { ww=wx; adj_wx=1; }
+
+@ This routine quickly tests if path |h| is ``essentially horizontal'' or
+``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
+allowable range for $x$ or~$y$. We do not need and cannot afford a full
+bounding-box computation.
+
+@<Declarations@>=
+static boolean mp_gr_coord_rangeOK (mp_knot *h,
+ quarterword zoff, scaled dz);
+
+@ @c
+boolean mp_gr_coord_rangeOK (mp_knot *h,
+ quarterword zoff, scaled dz) {
+ mp_knot *p; /* for scanning the path form |h| */
+ scaled zlo,zhi; /* coordinate range so far */
+ scaled z; /* coordinate currently being tested */
+ if (zoff==do_x_loc) {
+ zlo=gr_x_coord(h);
+ zhi=zlo;
+ p=h;
+ while ( gr_right_type(p)!=mp_endpoint ) {
+ z=gr_right_x(p);
+ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
+ p=gr_next_knot(p); z=gr_left_x(p);
+ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
+ z=gr_x_coord(p);
+ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
+ if ( p==h ) break;
+ }
+ } else {
+ zlo=gr_y_coord(h);
+ zhi=zlo;
+ p=h;
+ while ( gr_right_type(p)!=mp_endpoint ) {
+ z=gr_right_y(p);
+ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
+ p=gr_next_knot(p); z=gr_left_y(p);
+ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
+ z=gr_y_coord(p);
+ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
+ if ( p==h ) break;
+ }
+ }
+ return true;
+}
+
+@ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
+if ( z<zlo ) zlo=z;
+else if ( z>zhi ) zhi=z;
+if ( zhi-zlo>dz ) return false
+
+@ Filling with an elliptical pen is implemented via a combination of \&{stroke}
+and \&{fill} commands and a nontrivial dash pattern would interfere with this.
+@:stroke}{\&{stroke} command@>
+@:fill}{\&{fill} command@>
+Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
+a reference.
+
+@<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
+if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) {
+ hh=NULL;
+} else {
+ hh=gr_dash_p(p);
+}
+if ( hh==NULL ) {
+ if ( gs_dash_p!=NULL || gs_dash_init_done == false) {
+ mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
+ gs_dash_p=NULL;
+ gs_dash_init_done=true;
+ }
+} else if ( ! mp_gr_same_dashes(gs_dash_p,hh) ) {
+ @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
+}
+
+@ The original code had a check here to ensure that the result from
+|mp_take_scaled| did not go out of bounds.
+
+@<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
+{ gs_dash_p=hh;
+ if ( (gr_dash_p(p)==NULL) || (hh==NULL) || (hh->array==NULL)) {
+ mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
+ } else {
+ int i;
+ ps_room(28);
+ mp_ps_print(mp, " [");
+ for (i=0; *(hh->array+i) != -1;i++) {
+ ps_room(13);
+ mp_ps_print_scaled(mp, *(hh->array+i));
+ mp_ps_print_char(mp, ' ') ;
+ }
+ ps_room(22);
+ mp_ps_print(mp, "] ");
+ mp_ps_print_scaled(mp, hh->offset);
+ mp_ps_print_cmd(mp, " setdash"," sd");
+ }
+}
+
+@ @<Declarations@>=
+static boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ;
+
+@ This function test if |h| and |hh| represent the same dash pattern.
+
+@c
+boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) {
+ boolean ret=false;
+ int i = 0;
+ if ( h==hh ) ret=true;
+ else if ( (h==NULL)||(hh==NULL) ) ret=false;
+ else if ( h->offset!=hh->offset ) ret=false;
+ else if ( h->array == hh->array ) ret=true;
+ else if ( h->array == NULL || hh->array == NULL) ret=false;
+ else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
+ return ret;
+}
+
+@ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
+{
+ while (*(h->array+i)!=-1 &&
+ *(hh->array+i)!=-1 &&
+ *(h->array+i) == *(hh->array+i)) i++;
+ if (i>0) {
+ if (*(h->array+i)==-1 && *(hh->array+i) == -1)
+ ret=true;
+ }
+}
+
+@ When stroking a path with an elliptical pen, it is necessary to transform
+the coordinate system so that a unit circular pen will have the desired shape.
+To keep this transformation local, we enclose it in a
+$$\&{gsave}\ldots\&{grestore}$$
+block. Any translation component must be applied to the path being stroked
+while the rest of the transformation must apply only to the pen.
+If |fill_also=true|, the path is to be filled as well as stroked so we must
+insert commands to do this after giving the path.
+
+@<Declarations@>=
+static void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) ;
+
+@
+@c void mp_gr_stroke_ellipse (MP mp, mp_graphic_object *h, boolean fill_also) {
+ /* generate an elliptical pen stroke from object |h| */
+ scaled txx,txy,tyx,tyy; /* transformation parameters */
+ mp_knot *p; /* the pen to stroke with */
+ scaled d1,det; /* for tweaking transformation parameters */
+ integer s; /* also for tweaking transformation paramters */
+ boolean transformed; /* keeps track of whether gsave/grestore are needed */
+ transformed=false;
+ @<Use |pen_p(h)| to set the transformation parameters and give the initial
+ translation@>;
+ @<Tweak the transformation parameters so the transformation is nonsingular@>;
+ if (gr_type(h)==mp_fill_code) {
+ mp_gr_ps_path_out(mp, gr_path_p((mp_fill_object *)h));
+ } else {
+ mp_gr_ps_path_out(mp, gr_path_p((mp_stroked_object *)h));
+ }
+ if ( mp->internal[mp_procset]==0 ) {
+ if ( fill_also ) mp_ps_print_nl(mp, "gsave fill grestore");
+ @<Issue \ps\ commands to transform the coordinate system@>;
+ mp_ps_print(mp, " stroke");
+ if ( transformed ) mp_ps_print(mp, " grestore");
+ } else {
+ if ( fill_also ) mp_ps_print_nl(mp, "B"); else mp_ps_print_ln(mp);
+ if ( (txy!=0)||(tyx!=0) ) {
+ mp_ps_print(mp, " [");
+ mp_ps_pair_out(mp, txx,tyx);
+ mp_ps_pair_out(mp, txy,tyy);
+ mp_ps_print(mp, "0 0] t");
+ } else if ((txx!=unity)||(tyy!=unity) ) {
+ mp_ps_pair_out(mp,txx,tyy);
+ mp_ps_print(mp, " s");
+ };
+ mp_ps_print(mp, " S");
+ if ( transformed ) mp_ps_print(mp, " Q");
+ }
+ mp_ps_print_ln(mp);
+}
+
+@ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
+if (gr_type(h)==mp_fill_code) {
+ p=gr_pen_p((mp_fill_object *)h);
+} else {
+ p=gr_pen_p((mp_stroked_object *)h);
+}
+txx=gr_left_x(p);
+tyx=gr_left_y(p);
+txy=gr_right_x(p);
+tyy=gr_right_y(p);
+if ( (gr_x_coord(p)!=0)||(gr_y_coord(p)!=0) ) {
+ mp_ps_print_nl(mp, "");
+ mp_ps_print_cmd(mp, "gsave ","q ");
+ mp_ps_pair_out(mp, gr_x_coord(p), gr_y_coord(p));
+ mp_ps_print(mp, "translate ");
+ txx-=gr_x_coord(p);
+ tyx-=gr_y_coord(p);
+ txy-=gr_x_coord(p);
+ tyy-=gr_y_coord(p);
+ transformed=true;
+} else {
+ mp_ps_print_nl(mp, "");
+}
+@<Adjust the transformation to account for |gs_width| and output the
+ initial \&{gsave} if |transformed| should be |true|@>
+
+@ @<Adjust the transformation to account for |gs_width| and output the...@>=
+if ( gs_width!=unity ) {
+ if ( gs_width==0 ) {
+ txx=unity; tyy=unity;
+ } else {
+ txx=mp_make_scaled(mp, txx,gs_width);
+ txy=mp_make_scaled(mp, txy,gs_width);
+ tyx=mp_make_scaled(mp, tyx,gs_width);
+ tyy=mp_make_scaled(mp, tyy,gs_width);
+ };
+}
+if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
+ if ( (! transformed) ){
+ mp_ps_print_cmd(mp, "gsave ","q ");
+ transformed=true;
+ }
+}
+
+@ @<Issue \ps\ commands to transform the coordinate system@>=
+if ( (txy!=0)||(tyx!=0) ){
+ mp_ps_print_ln(mp);
+ mp_ps_print_char(mp, '[');
+ mp_ps_pair_out(mp, txx,tyx);
+ mp_ps_pair_out(mp, txy,tyy);
+ mp_ps_print(mp, "0 0] concat");
+} else if ( (txx!=unity)||(tyy!=unity) ){
+ mp_ps_print_ln(mp);
+ mp_ps_pair_out(mp, txx,tyy);
+ mp_ps_print(mp, "scale");
+}
+
+@ The \ps\ interpreter will probably abort if it encounters a singular
+transformation matrix. The determinant must be large enough to ensure that
+the printed representation will be nonsingular. Since the printed
+representation is always within $2^{-17}$ of the internal |scaled| value, the
+total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
+the magnitudes of |txx/65536|, |txy/65536|, etc.
+
+The |aspect_bound*(gs_width+1)| bound on the components of the pen
+transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
+
+@<Tweak the transformation parameters so the transformation is nonsingular@>=
+det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
+d1=4*aspect_bound+1;
+if ( abs(det)<d1 ) {
+ if ( det>=0 ) { d1=d1-det; s=1; }
+ else { d1=-d1-det; s=-1; };
+ d1=d1*unity;
+ if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
+ if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
+ else txx=txx+(d1+s*abs(tyy)) / tyy;
+ } else {
+ if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
+ else txy=txy+(d1+s*abs(tyx)) / tyx;
+ }
+}
+
+@ Here is a simple routine that just fills a cycle.
+
+@<Declarations@>=
+static void mp_gr_ps_fill_out (MP mp, mp_knot *p);
+
+@ @c
+void mp_gr_ps_fill_out (MP mp, mp_knot *p) { /* fill cyclic path~|p| */
+ mp_gr_ps_path_out(mp, p);
+ mp_ps_print_cmd(mp, " fill"," F");
+ mp_ps_print_ln(mp);
+}
+
+@ A text node may specify an arbitrary transformation but the usual case
+involves only shifting, scaling, and occasionally rotation. The purpose
+of |choose_scale| is to select a scale factor so that the remaining
+transformation is as ``nice'' as possible. The definition of ``nice''
+is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
+nice because they work out well for bitmap fonts. The code here selects
+a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
+non-shifting part of the transformation matrix. It is careful to avoid
+additions that might cause undetected overflow.
+
+@<Declarations@>=
+static scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) ;
+
+@ @c scaled mp_gr_choose_scale (MP mp, mp_graphic_object *p) {
+ /* |p| should point to a text node */
+ scaled a,b,c,d,ad,bc; /* temporary values */
+ a=gr_txx_val(p);
+ b=gr_txy_val(p);
+ c=gr_tyx_val(p);
+ d=gr_tyy_val(p);
+ if ( a<0 ) negate(a);
+ if ( b<0 ) negate(b);
+ if ( c<0 ) negate(c);
+ if ( d<0 ) negate(d);
+ ad=half(a-d);
+ bc=half(b-c);
+ return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
+}
+
+@ The potential overflow here is caused by the fact the returned value
+has to fit in a |name_type|, which is a quarterword.
+
+@d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
+
+@<Declarations@>=
+static quarterword mp_size_index (MP mp, font_number f, scaled s) ;
+
+@ @c
+quarterword mp_size_index (MP mp, font_number f, scaled s) {
+ pointer p,q; /* the previous and current font size nodes */
+ quarterword i; /* the size index for |q| */
+ q=mp->font_sizes[f];
+ i=0;
+ while ( q!=null ) {
+ if ( abs(s-sc_factor(q))<=fscale_tolerance )
+ return i;
+ else
+ { p=q; q=link(q); incr(i); };
+ if ( i==max_quarterword )
+ mp_overflow(mp, "sizes per font",max_quarterword);
+@:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
+ }
+ q=mp_get_node(mp, font_size_size);
+ sc_factor(q)=s;
+ if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
+ return i;
+}
+
+@ @<Declarations@>=
+static scaled mp_indexed_size (MP mp,font_number f, quarterword j);
+
+@ @c
+scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
+ pointer p; /* a font size node */
+ quarterword i; /* the size index for |p| */
+ p=mp->font_sizes[f];
+ i=0;
+ if ( p==null ) mp_confusion(mp, "size");
+ while ( (i!=j) ) {
+ incr(i); p=link(p);
+ if ( p==null ) mp_confusion(mp, "size");
+ }
+ return sc_factor(p);
+}
+
+@ @<Declarations@>=
+static void mp_clear_sizes (MP mp) ;
+
+@ @c void mp_clear_sizes (MP mp) {
+ font_number f; /* the font whose size list is being cleared */
+ pointer p; /* current font size nodes */
+ for (f=null_font+1;f<=mp->last_fnum;f++) {
+ while ( mp->font_sizes[f]!=null ) {
+ p=mp->font_sizes[f];
+ mp->font_sizes[f]=link(p);
+ mp_free_node(mp, p,font_size_size);
+ }
+ }
+}
+
+@ A text node may specify an arbitrary transformation but the usual case
+involves only shifting, scaling, and occasionally rotation. The purpose
+of |choose_scale| is to select a scale factor so that the remaining
+transformation is as ``nice'' as possible. The definition of ``nice''
+is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
+nice because they work out well for bitmap fonts. The code here selects
+a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
+non-shifting part of the transformation matrix. It is careful to avoid
+additions that might cause undetected overflow.
+
+
+@ @c static scaled mp_choose_scale (MP mp, mp_graphic_object *p) {
+ /* |p| should point to a text node */
+ scaled a,b,c,d,ad,bc; /* temporary values */
+ a=gr_txx_val(p);
+ b=gr_txy_val(p);
+ c=gr_tyx_val(p);
+ d=gr_tyy_val(p);
+ if ( (a<0) ) negate(a);
+ if ( (b<0) ) negate(b);
+ if ( (c<0) ) negate(c);
+ if ( (d<0) ) negate(d);
+ ad=half(a-d);
+ bc=half(b-c);
+ return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
+}
+
+@ There may be many sizes of one font and we need to keep track of the
+characters used for each size. This is done by keeping a linked list of
+sizes for each font with a counter in each text node giving the appropriate
+position in the size list for its font.
+
+@d font_size_size 2 /* size of a font size node */
+
+
+@ @<Declarations@>=
+static void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size);
+
+@ @c
+void mp_apply_mark_string_chars(MP mp, mp_edge_object *h, int next_size) {
+ mp_graphic_object * p;
+ p=h->body;
+ while ( p!= NULL ) {
+ if ( gr_type(p)==mp_text_code ) {
+ if ( gr_font_n(p)!=null_font ) {
+ if ( gr_size_index(p)==(unsigned char)next_size )
+ mp_mark_string_chars(mp, gr_font_n(p),gr_text_p(p),gr_text_l(p));
+ }
+ }
+ p=gr_link(p);
+ }
+}
+
+@ @<Unmark all marked characters@>=
+for (f=null_font+1;f<=mp->last_fnum;f++) {
+ if ( mp->font_sizes[f]!=null ) {
+ mp_unmark_font(mp, f);
+ mp->font_sizes[f]=null;
+ }
+}
+
+@ @<Scan all the text nodes and mark the used ...@>=
+p=hh->body;
+while ( p!=null ) {
+ if ( gr_type(p)==mp_text_code ) {
+ f = gr_font_n(p);
+ if (f!=null_font ) {
+ switch (prologues) {
+ case 2:
+ case 3:
+ mp->font_sizes[f] = mp_void;
+ mp_mark_string_chars(mp, f, gr_text_p(p),gr_text_l(p));
+ if (mp_has_fm_entry(mp,f,NULL) ) {
+ if (mp->font_enc_name[f]==NULL )
+ mp->font_enc_name[f] = mp_fm_encoding_name(mp,f);
+ mp->font_ps_name[f] = mp_fm_font_name(mp,f);
+ }
+ break;
+ case 1:
+ mp->font_sizes[f]=mp_void;
+ break;
+ default:
+ gr_size_index(p)=(unsigned char)mp_size_index(mp, f,mp_choose_scale(mp, p));
+ if ( gr_size_index(p)==0 )
+ mp_mark_string_chars(mp, f, gr_text_p(p),gr_text_l(p));
+ }
+ }
+ }
+ p=gr_link(p);
+}
+
+
+@
+@d pen_is_elliptical(A) ((A)==gr_next_knot((A)))
+
+@<Exported function ...@>=
+int mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset, int standalone) ;
+
+@ @c
+int mp_gr_ship_out (mp_edge_object *hh, int qprologues, int qprocset,int standalone) {
+ mp_graphic_object *p;
+ scaled ds,scf; /* design size and scale factor for a text node */
+ font_number f; /* for loops over fonts while (un)marking characters */
+ boolean transformed; /* is the coordinate system being transformed? */
+ int prologues, procset;
+ MP mp = hh->parent;
+ if (standalone) {
+ mp->jump_buf = malloc(sizeof(jmp_buf));
+ if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)))
+ return 0;
+ }
+ if (mp->history >= mp_fatal_error_stop ) return 1;
+ if (qprologues<0)
+ prologues = (int)((unsigned)mp->internal[mp_prologues]>>16);
+ else
+ prologues=qprologues;
+ if (qprocset<0)
+ procset = (int)((unsigned)mp->internal[mp_procset]>>16);
+ else
+ procset=qprocset;
+ mp_open_output_file(mp);
+ mp_print_initial_comment(mp, hh, prologues);
+ p = hh->body;
+ @<Unmark all marked characters@>;
+ if ( prologues==2 || prologues==3 ) {
+ mp_reload_encodings(mp);
+ }
+ @<Scan all the text nodes and mark the used characters@>;
+ if ( prologues==2 || prologues==3 ) {
+ mp_print_improved_prologue(mp, hh, prologues, procset);
+ } else {
+ mp_print_prologue(mp, hh, prologues, procset);
+ }
+ mp_gs_unknown_graphics_state(mp, 0);
+ p = hh->body;
+ while ( p!=NULL ) {
+ if ( gr_has_color(p) ) {
+ @<Write |pre_script| of |p|@>;
+ }
+ mp_gr_fix_graphics_state(mp, p);
+ switch (gr_type(p)) {
+ case mp_fill_code:
+ if ( gr_pen_p((mp_fill_object *)p)==NULL ) {
+ mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
+ } else if ( pen_is_elliptical(gr_pen_p((mp_fill_object *)p)) ) {
+ mp_gr_stroke_ellipse(mp, p,true);
+ } else {
+ mp_gr_ps_fill_out(mp, gr_path_p((mp_fill_object *)p));
+ mp_gr_ps_fill_out(mp, gr_htap_p(p));
+ }
+ if ( gr_post_script((mp_fill_object *)p)!=NULL ) {
+ mp_ps_print_nl (mp, gr_post_script((mp_fill_object *)p));
+ mp_ps_print_ln(mp);
+ }
+ break;
+ case mp_stroked_code:
+ if ( pen_is_elliptical(gr_pen_p((mp_stroked_object *)p)) )
+ mp_gr_stroke_ellipse(mp, p,false);
+ else {
+ mp_gr_ps_fill_out(mp, gr_path_p((mp_stroked_object *)p));
+ }
+ if ( gr_post_script((mp_stroked_object *)p)!=NULL ) {
+ mp_ps_print_nl (mp, gr_post_script((mp_stroked_object *)p));
+ mp_ps_print_ln(mp);
+ }
+ break;
+ case mp_text_code:
+ if ( (gr_font_n(p)!=null_font) && (gr_text_l(p)>0) ) {
+ if ( prologues>0 )
+ scf=mp_gr_choose_scale(mp, p);
+ else
+ scf=mp_indexed_size(mp, gr_font_n(p), (quarterword)gr_size_index(p));
+ @<Shift or transform as necessary before outputting text node~|p| at scale
+ factor~|scf|; set |transformed:=true| if the original transformation must
+ be restored@>;
+ mp_ps_string_out(mp, gr_text_p(p),gr_text_l(p));
+ mp_ps_name_out(mp, mp->font_name[gr_font_n(p)],false);
+ @<Print the size information and \ps\ commands for text node~|p|@>;
+ mp_ps_print_ln(mp);
+ }
+ if ( gr_post_script((mp_text_object *)p)!=NULL ) {
+ mp_ps_print_nl (mp, gr_post_script((mp_text_object *)p)); mp_ps_print_ln(mp);
+ }
+ break;
+ case mp_start_clip_code:
+ mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "gsave ","q ");
+ mp_gr_ps_path_out(mp, gr_path_p((mp_clip_object *)p));
+ mp_ps_print_cmd(mp, " clip"," W");
+ mp_ps_print_ln(mp);
+ if ( mp->internal[mp_restore_clip_color]>0 )
+ mp_gs_unknown_graphics_state(mp, 1);
+ break;
+ case mp_stop_clip_code:
+ mp_ps_print_nl(mp, ""); mp_ps_print_cmd(mp, "grestore","Q");
+ mp_ps_print_ln(mp);
+ if ( mp->internal[mp_restore_clip_color]>0 )
+ mp_gs_unknown_graphics_state(mp, 2);
+ else
+ mp_gs_unknown_graphics_state(mp, -1);
+ break;
+ case mp_start_bounds_code:
+ case mp_stop_bounds_code:
+ break;
+ case mp_special_code:
+ {
+ mp_special_object *ps = (mp_special_object *)p;
+ mp_ps_print_nl (mp, gr_pre_script(ps));
+ mp_ps_print_ln (mp);
+ }
+ break;
+ } /* all cases are enumerated */
+ p=gr_link(p);
+ }
+ mp_ps_print_cmd(mp, "showpage","P"); mp_ps_print_ln(mp);
+ mp_ps_print(mp, "%%EOF"); mp_ps_print_ln(mp);
+ (mp->close_file)(mp,mp->output_file);
+ if ( prologues<=0 )
+ mp_clear_sizes(mp);
+ return 1;
+}
+
+@ @(mplibps.h@>=
+int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) ;
+
+@ @c
+int mp_ps_ship_out (mp_edge_object *hh, int prologues, int procset) {
+ return mp_gr_ship_out (hh, prologues, procset, (int)true);
+}
+
+
+
+
+
+@
+@d do_write_prescript(a,b) {
+ if ( (gr_pre_script((b *)a))!=NULL ) {
+ mp_ps_print_nl (mp, gr_pre_script((b *)a));
+ mp_ps_print_ln(mp);
+ }
+}
+
+@<Write |pre_script| of |p|@>=
+{
+ if (gr_type(p)==mp_fill_code) { do_write_prescript(p,mp_fill_object); }
+ else if (gr_type(p)==mp_stroked_code) { do_write_prescript(p,mp_stroked_object); }
+ else if (gr_type(p)==mp_text_code) { do_write_prescript(p,mp_text_object); }
+}
+
+@ @<Print the size information and \ps\ commands for text node~|p|@>=
+ps_room(18);
+mp_ps_print_char(mp, ' ');
+ds=(mp->font_dsize[gr_font_n(p)]+8) / 16;
+mp_ps_print_scaled(mp, mp_take_scaled(mp, ds,scf));
+mp_ps_print(mp, " fshow");
+if ( transformed )
+ mp_ps_print_cmd(mp, " grestore"," Q")
+
+
+
+@ @<Shift or transform as necessary before outputting text node~|p| at...@>=
+transformed=(gr_txx_val(p)!=scf)||(gr_tyy_val(p)!=scf)||
+ (gr_txy_val(p)!=0)||(gr_tyx_val(p)!=0);
+if ( transformed ) {
+ mp_ps_print_cmd(mp, "gsave [", "q [");
+ mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txx_val(p),scf),
+ mp_make_scaled(mp, gr_tyx_val(p),scf));
+ mp_ps_pair_out(mp, mp_make_scaled(mp, gr_txy_val(p),scf),
+ mp_make_scaled(mp, gr_tyy_val(p),scf));
+ mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
+ mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
+} else {
+ mp_ps_pair_out(mp, gr_tx_val(p),gr_ty_val(p));
+ mp_ps_print_cmd(mp, "moveto","m");
+}
+mp_ps_print_ln(mp)
+
+@ @(mplibps.h@>=
+void mp_gr_toss_objects ( mp_edge_object *hh) ;
+void mp_gr_toss_object (mp_graphic_object *p) ;
+
+@ @c
+void mp_gr_toss_object (mp_graphic_object *p) {
+ mp_fill_object *tf;
+ mp_stroked_object *ts;
+ mp_text_object *tt;
+ switch (gr_type(p)) {
+ case mp_fill_code:
+ tf = (mp_fill_object *)p;
+ mp_xfree(gr_pre_script(tf));
+ mp_xfree(gr_post_script(tf));
+ mp_gr_toss_knot_list(mp,gr_pen_p(tf));
+ mp_gr_toss_knot_list(mp,gr_path_p(tf));
+ mp_gr_toss_knot_list(mp,gr_htap_p(p));
+ break;
+ case mp_stroked_code:
+ ts = (mp_stroked_object *)p;
+ mp_xfree(gr_pre_script(ts));
+ mp_xfree(gr_post_script(ts));
+ mp_gr_toss_knot_list(mp,gr_pen_p(ts));
+ mp_gr_toss_knot_list(mp,gr_path_p(ts));
+ if (gr_dash_p(p)!=NULL)
+ mp_gr_toss_dashes (mp,gr_dash_p(p));
+ break;
+ case mp_text_code:
+ tt = (mp_text_object *)p;
+ mp_xfree(gr_pre_script(tt));
+ mp_xfree(gr_post_script(tt));
+ mp_xfree(gr_text_p(p));
+ mp_xfree(gr_font_name(p));
+ break;
+ case mp_start_clip_code:
+ mp_gr_toss_knot_list(mp,gr_path_p((mp_clip_object *)p));
+ break;
+ case mp_start_bounds_code:
+ mp_gr_toss_knot_list(mp,gr_path_p((mp_bounds_object *)p));
+ break;
+ case mp_stop_clip_code:
+ case mp_stop_bounds_code:
+ break;
+ case mp_special_code:
+ mp_xfree(gr_pre_script((mp_special_object *)p));
+ break;
+ } /* all cases are enumerated */
+ mp_xfree(p);
+}
+
+
+@ @c
+void mp_gr_toss_objects (mp_edge_object *hh) {
+ mp_graphic_object *p, *q;
+ p = hh->body;
+ while ( p!=NULL ) {
+ q = gr_link(p);
+ mp_gr_toss_object(p);
+ p=q;
+ }
+ mp_xfree(hh->filename);
+ mp_xfree(hh);
+}
+
+@ @(mplibps.h@>=
+mp_graphic_object *mp_gr_copy_object (MP mp, mp_graphic_object *p) ;
+
+@ @c
+mp_graphic_object *
+mp_gr_copy_object (MP mp, mp_graphic_object *p) {
+ mp_fill_object *tf;
+ mp_stroked_object *ts;
+ mp_text_object *tt;
+ mp_clip_object *tc;
+ mp_bounds_object *tb;
+ mp_special_object *tp;
+ mp_graphic_object *q = NULL;
+ switch (gr_type(p)) {
+ case mp_fill_code:
+ tf = (mp_fill_object *)mp_new_graphic_object(mp, mp_fill_code);
+ gr_pre_script(tf) = mp_xstrdup(mp, gr_pre_script((mp_fill_object *)p));
+ gr_post_script(tf) = mp_xstrdup(mp, gr_post_script((mp_fill_object *)p));
+ gr_path_p(tf) = mp_gr_copy_path(mp,gr_path_p((mp_fill_object *)p));
+ gr_htap_p(tf) = mp_gr_copy_path(mp,gr_htap_p(p));
+ gr_pen_p(tf) = mp_gr_copy_path(mp,gr_pen_p((mp_fill_object *)p));
+ q = (mp_graphic_object *)tf;
+ break;
+ case mp_stroked_code:
+ ts = (mp_stroked_object *)mp_new_graphic_object(mp, mp_stroked_code);
+ gr_pre_script(ts) = mp_xstrdup(mp, gr_pre_script((mp_stroked_object *)p));
+ gr_post_script(ts) = mp_xstrdup(mp, gr_post_script((mp_stroked_object *)p));
+ gr_path_p(ts) = mp_gr_copy_path(mp,gr_path_p((mp_stroked_object *)p));
+ gr_pen_p(ts) = mp_gr_copy_path(mp,gr_pen_p((mp_stroked_object *)p));
+ gr_dash_p(ts) = mp_gr_copy_dashes(mp,gr_dash_p(p));
+ q = (mp_graphic_object *)ts;
+ break;
+ case mp_text_code:
+ tt = (mp_text_object *)mp_new_graphic_object(mp, mp_text_code);
+ gr_pre_script(tt) = mp_xstrdup(mp, gr_pre_script((mp_text_object *)p));
+ gr_post_script(tt) = mp_xstrdup(mp, gr_post_script((mp_text_object *)p));
+ gr_text_p(tt) = mp_xstrldup(mp, gr_text_p(p), gr_text_l(p));
+ gr_text_l(tt) = gr_text_l(p);
+ gr_font_name(tt) = mp_xstrdup(mp, gr_font_name(p));
+ q = (mp_graphic_object *)tt;
+ break;
+ case mp_start_clip_code:
+ tc = (mp_clip_object *)mp_new_graphic_object(mp, mp_start_clip_code);
+ gr_path_p(tc) = mp_gr_copy_path(mp,gr_path_p((mp_clip_object *)p));
+ q = (mp_graphic_object *)tc;
+ break;
+ case mp_start_bounds_code:
+ tb = (mp_bounds_object *)mp_new_graphic_object(mp, mp_start_bounds_code);
+ gr_path_p(tb) = mp_gr_copy_path(mp,gr_path_p((mp_bounds_object *)p));
+ q = (mp_graphic_object *)tb;
+ break;
+ case mp_special_code:
+ tp = (mp_special_object *)mp_new_graphic_object(mp, mp_special_code);
+ gr_pre_script(tp) = mp_xstrdup(mp, gr_pre_script((mp_special_object *)p));
+ q = (mp_graphic_object *)tp;
+ break;
+ case mp_stop_clip_code:
+ q = mp_new_graphic_object(mp, mp_stop_clip_code);
+ break;
+ case mp_stop_bounds_code:
+ q = mp_new_graphic_object(mp, mp_stop_bounds_code);
+ break;
+ } /* all cases are enumerated */
+ return q;
+}
+