1
% This file is part of MetaPost;
2
% the MetaPost program is in the public domain.
3
% See the <Show version...> code in mpost.w for more info.
5
\def\title{Creating mpx files}
6
\def\hang{\hangindent 3em\indent\ignorespaces}
8
\def\LaTeX{{\rm L\kern-.36em\raise.3ex\hbox{\sc a}\kern-.15em
9
T\kern-.1667em\lower.7ex\hbox{E}\kern-.125emX}}
11
\def\(#1){} % this is used to make section names sort themselves better
12
\def\9#1{} % this is used for sort keys in the index
17
@* \[1] Makempx overview.
19
This source file implements the makempx functionality for the new \MP.
20
It includes all of the functional code from the old standalone programs
27
combined into one, with many changes to make all of the code cooperate
32
The local C preprocessor definitions have to come after the C includes
33
in order to prevent name clashes.
36
#include <w2c/config.h>
43
#include <errno.h> /* TODO autoconf ? */
44
/* unistd.h is needed for every non-Win32 platform, and we assume
45
* that implies that sys/types.h is also present
48
#include <sys/types.h>
57
# include <sys/wait.h>
60
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
63
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
73
# define dirent direct
75
# include <sys/ndir.h>
91
#define trunc(x) ((integer) (x))
92
#define fabs(x) ((x)<0?(-(x)):(x))
93
#define floor(x) ((integer) (fabs(x)))
95
#define PI 3.14159265358979323846
103
From the Pascal code of DVItoMP two implicit types are inherited: |web_boolean| and
106
The more complex datatypes are defined in the following sections.
112
typedef signed int web_integer;
113
typedef signed int web_boolean;
117
@ The single most important data structure is the structure
118
|mpx_data|. It contains all of the global state for a specific
119
|makempx| run. A pointer to it is passed as the first argument to just
120
about every function call.
122
One of the fields is a bit special because it is so important: |mode|
123
is the decider between running \TeX\ or Troff as the typesetting
133
typedef struct mpx_data * MPX;
134
@<Makempx header information@>
138
@<Types in the outer block@>
139
typedef struct mpx_data {
144
@ Here are some macros for common programming idioms.
146
@d MAXINT 0x7FFFFF /* somewhat arbitrary */
148
@d incr(A) (A)=(A)+1 /* increase a variable by unity */
149
@d decr(A) (A)=(A)-1 /* decrease a variable by unity */
151
@ Once an MPX object is allocated, the memory it occupies needs to be
152
initialized to a usable state. This procedure gets things started
155
This function is not allowed to run |mpx_abort| because at this
156
point the jump buffer is not yet initialized, so it should only
157
be used for things that cannot go wrong!
160
static void mpx_initialize (MPX mpx) {
161
memset(mpx,0,sizeof(struct mpx_data));
162
@<Set initial values@>@/
165
@ A global variable |history| keeps track of what type of errors have
166
occurred with the hope that that \MP\ can be warned of any problems.
169
enum mpx_history_states {
170
mpx_spotless=0, /* |history| value when no problems have been found */
171
mpx_cksum_trouble, /* |history| value there have been font checksum mismatches */
172
mpx_warning_given, /* |history| value after a recoverable error */
173
mpx_fatal_error /* |history| value if processing had to be aborted */
181
mpx->history=mpx_spotless;
183
@ The structure has room for the names and the |FILE *| for the
184
input and output files. The most important ones are listed here,
185
the variables for the intermediate files are declared where they
195
int lnno ; /* current line number */
197
@ A set of basic reporting functions.
200
static void mpx_printf(MPX mpx, const char *header, const char *msg, va_list ap) {
201
fprintf(mpx->errfile, "makempx %s: %s:", header, mpx->mpname);
203
fprintf(mpx->errfile, "%d:", mpx->lnno);
204
fprintf(mpx->errfile, " ");
205
(void)vfprintf(mpx->errfile, msg, ap);
206
fprintf(mpx->errfile, "\n");
210
static void mpx_report(MPX mpx, const char *msg, ...) {
212
if (mpx->debug==0) return;
214
mpx_printf(mpx, "debug", msg, ap);
216
if ( mpx->history < mpx_warning_given )
217
mpx->history=mpx_cksum_trouble;
221
static void mpx_warn(MPX mpx, const char *msg, ...) {
224
mpx_printf(mpx, "warning", msg, ap);
226
if ( mpx->history < mpx_warning_given )
227
mpx->history=mpx_cksum_trouble;
231
static void mpx_error(MPX mpx, const char *msg, ...) {
234
mpx_printf(mpx, "error", msg, ap);
236
mpx->history=mpx_warning_given;
239
@ The program uses a |jump_buf| to handle non-local returns,
240
this is initialized at a single spot: the start of |mp_makempx|.
242
@d mpx_jump_out longjmp(mpx->jump_buf,1)
249
static void mpx_abort(MPX mpx, const char *msg, ...) {
252
fprintf(stderr, "fatal: ");
253
(void)vfprintf(stderr, msg, ap);
256
mpx_printf(mpx, "fatal", msg, ap);
258
mpx->history=mpx_fatal_error;
263
@ @<Install and test the non-local jump buffer@>=
264
if (setjmp(mpx->jump_buf) != 0) {
265
int h = mpx->history;
275
static FILE *mpx_xfopen (MPX mpx, const char *fname, const char *fmode) {
276
FILE *f = fopen(fname,fmode);
278
mpx_abort(mpx,"File open error for %s in mode %s", fname, fmode);
281
static void mpx_fclose (MPX mpx, FILE *file) {
287
@d xfree(A) do { mpx_xfree(A); A=NULL; } while (0)
288
@d xrealloc(P,A,B) mpx_xrealloc(mpx,P,A,B)
289
@d xmalloc(A,B) mpx_xmalloc(mpx,A,B)
290
@d xstrdup(A) mpx_xstrdup(mpx,A)
293
static void mpx_xfree (void *x);
294
static void *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) ;
295
static void *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) ;
296
static char *mpx_xstrdup(MPX mpX, const char *s);
299
@ The |max_size_test| guards against overflow, on the assumption that
300
|size_t| is at least 31bits wide.
302
@d max_size_test 0x7FFFFFFF
305
static void mpx_xfree (void *x) {
306
if (x!=NULL) free(x);
308
static void *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) {
310
if ((max_size_test/size)<nmem) {
311
mpx_abort(mpx,"Memory size overflow");
313
w = realloc (p,(nmem*size));
314
if (w==NULL) mpx_abort(mpx,"Out of Memory");
317
static void *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) {
319
if ((max_size_test/size)<nmem) {
320
mpx_abort(mpx,"Memory size overflow");
322
w = malloc (nmem*size);
323
if (w==NULL) mpx_abort(mpx,"Out of Memory");
326
static char *mpx_xstrdup(MPX mpx, const char *s) {
331
if (w==NULL) mpx_abort(mpx,"Out of Memory");
334
@* The command 'newer' became a function.
336
We may have high-res timers in struct stat. If we do, use them.
339
static int mpx_newer(char *source, char *target) {
340
struct stat source_stat, target_stat;
342
if (stat(target, &target_stat) < 0) return 0; /* true */
343
if (stat(source, &source_stat) < 0) return 1; /* false */
344
#if HAVE_STRUCT_STAT_ST_MTIM
345
if (source_stat.st_mtim.tv_sec > target_stat.st_mtim.tv_sec ||
346
(source_stat.st_mtim.tv_sec == target_stat.st_mtim.tv_sec &&
347
source_stat.st_mtim.tv_nsec >= target_stat.st_mtim.tv_nsec))
350
if (source_stat.st_mtime >= target_stat.st_mtime)
359
@* Extracting data from \MP\ input.
361
This part of the program transforms a \MP\ input file into a \TeX\ or
362
troff input file by stripping out \.{btex}$\ldots$\.{etex} and
363
\.{verbatimtex}$\ldots$\.{etex} sections.
364
Leading and trailing spaces and tabs are removed from the
365
extracted material and it is surrounded by the preceding and following
366
strings defined immediately below. The input file should be given as
367
argument 1 and the resulting \TeX\ or troff file is written on standard
370
John Hobby wrote the original version, which has since been
371
extensively altered. The current implementation is a bit trickier
372
than I would like, but changing it will take careful study and
373
will likely make it run slower, so I've left it as-is for now.
376
int texcnt ; /* btex..etex blocks so far */
377
int verbcnt ; /* verbatimtex..etex blocks so far */
378
char *bb, *tt, *aa; /* start of before, token, and after strings */
379
char *buf; /* the input line */
382
@ @<Set initial values@>=
385
@ This function returns NULL on EOF, otherwise it returns |buf|.
388
static char *mpx_getline(MPX mpx, FILE *mpfile) {
394
mpx->buf = xmalloc(mpx->bufsize,1);
395
while ((c = getc(mpfile)) != EOF && c != '\n' && c != '\r') {
396
mpx->buf[loc++] = (char)c;
397
if (loc == mpx->bufsize) {
398
char *temp = mpx->buf;
399
unsigned n = mpx->bufsize + (mpx->bufsize>>4);
401
mpx_abort(mpx,"Line is too long");
402
mpx->buf = xmalloc(n,1);
403
memcpy(mpx->buf,temp,mpx->bufsize);
419
@ Return nonzero if a prefix of string $s$ matches the null-terminated string $t$
420
and the next character is not a letter or an underscore.
423
static int mpx_match_str(const char *s, const char *t) {
430
if ((*s>= 'a' && *s<='z') || (*s>= 'A' && *s<='Z') || *s == '_')
436
@ This function tries to express $s$ as the concatenation of three
437
strings $b$, $t$, $a$, with the global pointers $bb$, $tt$, and $aa$ set to the
438
start of the corresponding strings. String $t$ is either a quote mark,
439
a percent sign, or an alphabetic token \.{btex}, \.{etex}, or
440
\.{verbatimtex}. (An alphabetic token is a maximal sequence of letters
441
and underscores.) If there are several possible substrings $t$, we
442
choose the leftmost one. If there is no such $t$, we set $b=s$ and return 0.
444
Various values are defined, so that |mpx_copy_mpto| can distinguish between
445
\.{verbatimtex} ... \.{etex} and \.{btex} ... \.{etex} (the former has no
446
whitespace corrections applied).
450
@d FIRST_VERBATIM_TEX 3
453
static int mpx_getbta(MPX mpx, char *s) {
454
int ok = 1; /* zero if last character was |a-z|, |A-Z|, or |_| */
461
for (mpx->tt = mpx->bb; *(mpx->tt) != 0; mpx->tt++) {
462
switch (*(mpx->tt)) {
465
mpx->aa = mpx->tt + 1;
468
if (ok && mpx_match_str(mpx->tt, "btex")) {
469
mpx->aa = mpx->tt + 4;
476
if (ok && mpx_match_str(mpx->tt, "etex")) {
477
mpx->aa = mpx->tt + 4;
484
if (ok && mpx_match_str(mpx->tt, "verbatimtex")) {
485
mpx->aa = mpx->tt + 11;
492
if ((*(mpx->tt) >= 'a' && *(mpx->tt) <= 'z') ||
493
(*(mpx->tt) >= 'A' && *(mpx->tt) <= 'Z') ||
505
static void mpx_copy_mpto (MPX mpx, FILE *outfile, int textype) {
506
char *s; /* where a string to print stops */
507
char *t; /* for finding start of last line */
512
if (mpx->aa == NULL || *mpx->aa == 0) {
513
if ((mpx->aa = mpx_getline(mpx,mpx->mpfile)) == NULL) {
514
mpx_error(mpx,"btex section does not end");
518
if (mpx_getbta(mpx, mpx->aa) && *(mpx->tt) == 'e') {
521
if (mpx->tt == NULL) {
522
mpx_error(mpx,"btex section does not end");
524
} else if (*(mpx->tt) == 'b') {
525
mpx_error(mpx,"btex in TeX mode");
527
} else if (*(mpx->tt) == 'v') {
528
mpx_error(mpx,"verbatimtex in TeX mode");
536
res = xmalloc(strlen(mpx->bb)+2,1);
537
res = strncpy(res,mpx->bb,(strlen(mpx->bb)+1));
539
res = xrealloc(res,strlen(res)+strlen(mpx->bb)+2,1);
540
res = strncat(res,mpx->bb, strlen(mpx->bb));
543
res = strncat(res, "\n", 1);
545
} while (*(mpx->tt) != 'e');
547
if (textype == B_TEX) {
548
/* whitespace at the end */
549
for (s = res + strlen(res) - 1;
550
s >= res && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'); s--);
556
if (textype == B_TEX || textype == FIRST_VERBATIM_TEX) {
557
/* whitespace at the start */
559
s < (res + strlen(res)) && (*s == ' ' || *s == '\t' || *s == '\r'
560
|| *s == '\n'); s++);
561
for (; *t != '\n' && t > s; t--);
563
fprintf(outfile,"%s", s);
564
if (textype == B_TEX) {
565
/* put no |%| at end if it's only 1 line total, starting with |%|;
566
* this covers the special case |%&format| in a single line. */
567
if (t != s || *t != '%')
568
fprintf(outfile,"%%");
574
@ Static strings for mpto
577
static const char *mpx_predoc[] = {"", ".po 0\n"};
578
static const char *mpx_postdoc[] = { "\\end{document}\n", ""};
579
static const char *mpx_pretex1[] = {
580
"\\gdef\\mpxshipout{\\shipout\\hbox\\bgroup%\n"
581
" \\setbox0=\\hbox\\bgroup}%\n"
582
"\\gdef\\stopmpxshipout{\\egroup"
583
" \\dimen0=\\ht0 \\advance\\dimen0\\dp0\n"
584
" \\dimen1=\\ht0 \\dimen2=\\dp0\n"
585
" \\setbox0=\\hbox\\bgroup\n"
587
" \\ifnum\\dimen0>0 \\vrule width1sp height\\dimen1 depth\\dimen2 \n"
588
" \\else \\vrule width1sp height1sp depth0sp\\relax\n"
590
" \\ht0=0pt \\dp0=0pt \\box0 \\egroup}\n"
591
"\\mpxshipout%% line %d %s\n", ".lf %d %s\n" };
592
static const char *mpx_pretex[] = { "\\mpxshipout%% line %d %s\n", ".bp\n.lf %d %s\n" };
593
static const char *mpx_posttex[] = { "\n\\stopmpxshipout\n", "\n" };
594
static const char *mpx_preverb1[] = {"", ".lf %d %s\n" }; /* if very first instance */
595
static const char *mpx_preverb[] = { "%% line %d %s\n", ".lf %d %s\n"}; /* all other instances */
596
static const char *mpx_postverb[] = { "\n", "\n" } ;
599
static void mpx_mpto(MPX mpx, char *tmpname, char *mptexpre) {
601
int verbatim_written = 0;
602
int mode = mpx->mode;
603
char *mpname = mpx->mpname;
604
if (mode==mpx_tex_mode) {
605
TMPNAME_EXT(mpx->tex,".tex");
607
TMPNAME_EXT(mpx->tex,".i");
609
outfile = mpx_xfopen(mpx,mpx->tex, "wb");
610
if (mode==mpx_tex_mode) {
612
if ((fr = fopen(mptexpre, "r"))!= NULL) {
615
while ((i=fread((void *)buf, 1, 512 , fr))>0) {
616
fwrite((void *)buf,1, i, outfile);
621
mpx->mpfile = mpx_xfopen(mpx,mpname, "r");
622
fprintf(outfile,"%s", mpx_predoc[mode]);
623
while (mpx_getline(mpx, mpx->mpfile) != NULL)
625
fprintf(outfile,"%s", mpx_postdoc[mode]);
626
mpx_fclose(mpx,mpx->mpfile);
627
mpx_fclose(mpx,outfile);
635
while (mpx_getbta(mpx, mpx->aa)) {
636
if (*(mpx->tt) == '%') {
638
} else if (*(mpx->tt) == '"') {
640
if (!mpx_getbta(mpx, mpx->aa))
641
mpx_error(mpx,"string does not end");
642
} while (*(mpx->tt) != '"');
643
} else if (*(mpx->tt) == 'b') {
644
if (mpx->texcnt++ == 0)
645
fprintf(outfile,mpx_pretex1[mode], mpx->lnno, mpname);
647
fprintf(outfile,mpx_pretex[mode], mpx->lnno, mpname);
648
mpx_copy_mpto(mpx, outfile, B_TEX);
649
fprintf(outfile,"%s", mpx_posttex[mode]);
650
} else if (*(mpx->tt) == 'v') {
651
if (mpx->verbcnt++ == 0 && mpx->texcnt == 0)
652
fprintf(outfile,mpx_preverb1[mode], mpx->lnno, mpname);
654
fprintf(outfile,mpx_preverb[mode], mpx->lnno, mpname);
655
if (!verbatim_written)
656
mpx_copy_mpto(mpx, outfile, FIRST_VERBATIM_TEX);
658
mpx_copy_mpto(mpx, outfile, VERBATIM_TEX);
659
fprintf(outfile,"%s", mpx_postverb[mode]);
661
mpx_error(mpx,"unmatched etex");
663
verbatim_written = 1;
667
@ @<Run |mpto| on the mp file@>=
668
mpx_mpto(mpx, tmpname, mpxopt->mptexpre)
670
@* DVItoMP Processing.
672
The \.{DVItoMP} program reads binary device-independent (``\.{DVI}'')
673
files that are produced by document compilers such as \TeX, and converts them
674
into a symbolic form understood by \MP. It is loosely based on the \.{DVItype}
675
utility program that produces a more faithful symbolic form of a \.{DVI} file.
677
The output file is a sequence of \MP\ picture expressions, one for every page
678
in the \.{DVI} file. It makes no difference to \.{DVItoMP} where the \.{DVI}
679
file comes from, but it is intended to process the result of running \TeX\
680
or \LaTeX\ on the output of the extraction process that is defined above.
681
Such a \.{DVI} file will contain one page for every \.{btex}$\ldots$\.{etex}
682
block in the original input. Processing with \.{DVItoMP} creates a
683
corresponding sequence of \MP\ picture expressions for use as an auxiliary
684
input file. Since \MP\ expects such files to have the extension \.{.MPX},
685
the output of \.{DVItoMP} is sometimes called an ``\.{MPX}'' file.
687
@ The following parameters can be changed at compile time to extend or
688
reduce \.{DVItoMP}'s capacity.
690
TODO: dynamic reallocation
692
@d virtual_space 1000000 /* maximum total bytes of typesetting commands for virtual fonts */
693
@d max_fonts 1000 /* maximum number of distinct fonts per \.{DVI} file */
694
@d max_fnums 3000 /* maximum number of fonts plus fonts local to virtual fonts */
695
@d max_widths (256*max_fonts) /* maximum number of different characters among all fonts */
696
@d line_length 79 /* maximum output line length (must be at least 60) */
697
@d stack_size 100 /* \.{DVI} files shouldn't |push| beyond this depth */
698
@d font_tolerance 0.00001
699
/* font sizes should match to within this multiple of $2^{20}$ \.{DVI} units */
701
@ If the \.{DVI} file is badly malformed, the whole process must be aborted;
702
\.{DVItoMP} will give up, after issuing an error message about the symptoms
705
@d bad_dvi(A) mpx_abort(mpx,"Bad DVI file: " A "!")
706
@d bad_dvi_two(A,B) mpx_abort(mpx,"Bad DVI file: " A "!", B)
709
@* The character set.
711
Like all programs written with the \.{WEB} system, \.{DVItoMP} can be
712
used with any character set. It an identify transfrom internally, because
713
the programming for portable input-output is easier when a fixed internal
714
code is used, and because \.{DVI} files use ASCII code for file names.
716
In the conversion from Pascal to C, the |xchr| array has been removed.
717
Because some systems may still want to change the input--output character
718
set, the accesses to |xchr| and |printable| are replaced by macro calls.
720
@d printable(c) (isprint(c) && c < 128 && c!='"')
724
static void mpx_open_mpxfile (MPX mpx) { /* prepares to write text on |mpxfile| */
725
mpx->mpxfile = mpx_xfopen (mpx,mpx->mpxname, "wb");
728
@* Device-independent file format.
729
The format of \.{DVI} files is described in many places including
730
\.{dvitype.web} and Volume~B of D.~E. Knuth's {\sl Computers and Typesetting}.
731
This program refers to the following command codes.
733
@d id_byte 2 /* identifies the kind of \.{DVI} files described here */
735
@d set_char_0 0 /* typeset character 0 and move right */
736
@d set1 128 /* typeset a character and move right */
737
@d set_rule 132 /* typeset a rule and move right */
738
@d put1 133 /* typeset a character */
739
@d put_rule 137 /* typeset a rule */
740
@d nop 138 /* no operation */
741
@d bop 139 /* beginning of page */
742
@d eop 140 /* ending of page */
743
@d push 141 /* save the current positions */
744
@d pop 142 /* restore previous positions */
745
@d right1 143 /* move right */
746
@d w0 147 /* move right by |w| */
747
@d w1 148 /* move right and set |w| */
748
@d x0 152 /* move right by |x| */
749
@d x1 153 /* move right and set |x| */
750
@d down1 157 /* move down */
751
@d y0 161 /* move down by |y| */
752
@d y1 162 /* move down and set |y| */
753
@d z0 166 /* move down by |z| */
754
@d z1 167 /* move down and set |z| */
755
@d fnt_num_0 171 /* set current font to 0 */
756
@d fnt1 235 /* set current font */
757
@d xxx1 239 /* extension to \.{DVI} primitives */
758
@d xxx4 242 /* potentially long extension to \.{DVI} primitives */
759
@d fnt_def1 243 /* define the meaning of a font number */
760
@d pre 247 /* preamble */
761
@d post 248 /* postamble beginning */
762
@d post_post 249 /* postamble ending */
763
@d undefined_commands 250: case 251: case 252: case 253: case 254: case 255
765
@* Input from binary files.
767
@ The program deals with two binary file variables: |dvi_file| is the main
768
input file that we are translating into symbolic form, and |tfm_file| is
769
the current font metric file from which character-width information is
770
being read. It is convenient to have a throw-away variable for function
771
results when reading parts of the files that are being skipped.
774
FILE * dvi_file; /* the input file */
775
FILE * tfm_file; /* a font metric file */
776
FILE * vf_file; /* a virtual font file */
778
@ Prepares to read packed bytes in |dvi_file|
780
static void mpx_open_dvi_file (MPX mpx) {
781
mpx->dvi_file = fopen(mpx->dviname,"rb");
782
if (mpx->dvi_file==NULL)
783
mpx_abort(mpx,"DVI generation failed");
786
@ Prepares to read packed bytes in |tfm_file|
788
static web_boolean mpx_open_tfm_file (MPX mpx) {
789
mpx->tfm_file = mpx_fsearch(mpx, mpx->cur_name, mpx_tfm_format);
790
if (mpx->tfm_file == NULL)
791
mpx_abort(mpx,"Cannot find TFM %s", mpx->cur_name);
792
free (mpx->cur_name); /* We |xmalloc|'d this before we got called. */
793
return true; /* If we get here, we succeeded. */
796
@ Prepares to read packed bytes in |vf_file|.
797
It's ok if the \.{VF} file doesn't exist.
800
static web_boolean mpx_open_vf_file (MPX mpx) {
801
mpx->vf_file = mpx_fsearch(mpx, mpx->cur_name, mpx_vf_format);
803
free (mpx->cur_name);
809
@ If you looked carefully at the preceding code, you probably asked,
810
``What is |cur_name|?'' Good question. It's a global
811
variable: |cur_name| is a string variable that will be set to the
812
current font metric file name before |open_tfm_file| or |open_vf_file|
816
char *cur_name; /* external name */
818
@ It turns out to be convenient to read four bytes at a time, when we are
819
inputting from \.{TFM} files. The input goes into global variables
820
|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
824
int b0, b1, b2, b3; /* four bytes input at once */
826
@ The |read_tfm_word| procedure sets |b0| through |b3| to the next
827
four bytes in the current \.{TFM} file.
830
static void mpx_read_tfm_word (MPX mpx) {
831
mpx->b0 = getc(mpx->tfm_file);
832
mpx->b1 = getc(mpx->tfm_file);
833
mpx->b2 = getc(mpx->tfm_file);
834
mpx->b3 = getc(mpx->tfm_file);
837
@ Input can come from from three different sources depending on the settings
838
of global variables. When |vf_reading| is true, we read from the \.{VF} file.
839
Otherwise, input can either come directly from |dvi_file| or from a buffer
840
|cmd_buf|. The latter case applies whenever |buf_ptr<virtual_space|.
843
web_boolean vf_reading; /* should input come from |vf_file|? */
844
unsigned char cmd_buf[(virtual_space+1)]; /* commands for virtual characters */
845
unsigned int buf_ptr; /* |cmd_buf| index for the next byte */
848
mpx->vf_reading=false;
849
mpx->buf_ptr=virtual_space;
851
@ We shall use a set of simple functions to read the next byte or bytes from the
852
current input source. There are seven possibilities, each of which is treated
853
as a separate function in order to minimize the overhead for subroutine calls.
856
static web_integer mpx_get_byte (MPX mpx) { /* returns the next byte, unsigned */
858
@<Read one byte into |b|@>;
862
static web_integer mpx_signed_byte (MPX mpx) { /* returns the next byte, signed */
864
@<Read one byte into |b|@>;
865
return ( b<128 ? b : (b-256));
868
static web_integer mpx_get_two_bytes (MPX mpx) { /* returns the next two bytes, unsigned */
870
a=0; b=0; /* for compiler warnings */
871
@<Read two bytes into |a| and |b|@>;
872
return (a*(int)(256)+b);
875
static web_integer mpx_signed_pair (MPX mpx) { /* returns the next two bytes, signed */
877
a=0; b=0; /* for compiler warnings */
878
@<Read two bytes into |a| and |b|@>;
879
if ( a<128 ) return (a*256+b);
880
else return ((a-256)*256+b);
883
static web_integer mpx_get_three_bytes (MPX mpx) { /* returns the next three bytes, unsigned */
885
a=0; b=0; c=0; /* for compiler warnings */
886
@<Read three bytes into |a|, |b|, and~|c|@>;
887
return ((a*(int)(256)+b)*256+c);
890
static web_integer mpx_signed_trio (MPX mpx) { /* returns the next three bytes, signed */
892
a=0; b=0; c=0; /* for compiler warnings */
893
@<Read three bytes into |a|, |b|, and~|c|@>;
894
if ( a<128 ) return ((a*(int)(256)+b)*256+c);
895
else return (((a-(int)(256))*256+b)*256+c);
898
static web_integer mpx_signed_quad (MPX mpx) { /* returns the next four bytes, signed */
899
unsigned char a,b,c,d;
900
a=0; b=0; c=0; d=0; /* for compiler warnings */
901
@<Read four bytes into |a|, |b|, |c|, and~|d|@>;
902
if ( a<128 ) return (((a*(int)(256)+b)*256+c)*256+d);
903
else return ((((a-256)*(int)(256)+b)*256+c)*256+d);
906
@ @<Read one byte into |b|@>=
907
if ( mpx->vf_reading ) {
908
b = (unsigned char)getc(mpx->vf_file);
909
} else if ( mpx->buf_ptr==virtual_space ) {
910
b = (unsigned char)getc(mpx->dvi_file);
912
b=mpx->cmd_buf[mpx->buf_ptr];
916
@ @<Read two bytes into |a| and |b|@>=
917
if ( mpx->vf_reading ) {
918
a = (unsigned char)getc(mpx->vf_file);
919
b = (unsigned char)getc(mpx->vf_file);
920
} else if ( mpx->buf_ptr==virtual_space ) {
921
a = (unsigned char)getc(mpx->dvi_file);
922
b = (unsigned char)getc(mpx->dvi_file);
923
} else if ( mpx->buf_ptr+2>mpx->n_cmds ) {
924
mpx_abort(mpx,"Error detected while interpreting a virtual font");
925
@.Error detected while...@>
927
a=mpx->cmd_buf[mpx->buf_ptr];
928
b=mpx->cmd_buf[mpx->buf_ptr+1];
932
@ @<Read three bytes into |a|, |b|, and~|c|@>=
933
if ( mpx->vf_reading ) {
934
a = (unsigned char)getc(mpx->vf_file);
935
b = (unsigned char)getc(mpx->vf_file);
936
c = (unsigned char)getc(mpx->vf_file);
937
} else if ( mpx->buf_ptr==virtual_space ) {
938
a = (unsigned char)getc(mpx->dvi_file);
939
b = (unsigned char)getc(mpx->dvi_file);
940
c = (unsigned char)getc(mpx->dvi_file);
941
} else if ( mpx->buf_ptr+3>mpx->n_cmds ) {
942
mpx_abort(mpx,"Error detected while interpreting a virtual font");
943
@.Error detected while...@>
945
a=mpx->cmd_buf[mpx->buf_ptr];
946
b=mpx->cmd_buf[mpx->buf_ptr+1];
947
c=mpx->cmd_buf[mpx->buf_ptr+2];
951
@ @<Read four bytes into |a|, |b|, |c|, and~|d|@>=
952
if ( mpx->vf_reading ) {
953
a = (unsigned char)getc(mpx->vf_file);
954
b = (unsigned char)getc(mpx->vf_file);
955
c = (unsigned char)getc(mpx->vf_file);
956
d = (unsigned char)getc(mpx->vf_file);
957
} else if ( mpx->buf_ptr==virtual_space ) {
958
a = (unsigned char)getc(mpx->dvi_file);
959
b = (unsigned char)getc(mpx->dvi_file);
960
c = (unsigned char)getc(mpx->dvi_file);
961
d = (unsigned char)getc(mpx->dvi_file);
962
} else if ( mpx->buf_ptr+4>mpx->n_cmds ) {
963
mpx_abort(mpx,"Error detected while interpreting a virtual font");
964
@.Error detected while...@>
966
a=mpx->cmd_buf[mpx->buf_ptr];
967
b=mpx->cmd_buf[mpx->buf_ptr+1];
968
c=mpx->cmd_buf[mpx->buf_ptr+2];
969
d=mpx->cmd_buf[mpx->buf_ptr+3];
973
@* Data structures for fonts.
975
\.{DVI} file format does not include information about character widths, since
976
that would tend to make the files a lot longer. But a program that reads
977
a \.{DVI} file is supposed to know the widths of the characters that appear
978
in \\{set\_char} commands. Therefore \.{DVItoMP} looks at the font metric
979
(\.{TFM}) files for the fonts that are involved.
982
@ For purposes of this program, the only thing we need to know about a
983
given character |c| in a non-virtual font |f| is the width. For the font as
984
a whole, all we need is the symbolic name to use in the \.{MPX} file.
986
This information appears implicitly in the following data
987
structures. The current number of fonts defined is |nf|. Each such font has
988
an internal number |f|, where |0<=f<nf|. There is also an external number
989
that identifies the font in the \.{DVI} file. The correspondence is
990
maintained in arrays |font_num| and |internal_num| so that |font_num[i]|
991
is the external number for |f=internal_num[i]|.
992
The external name of this font is the string that occupies |font_name[f]|.
993
The legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive.
994
The \.{TFM} file can specify that some of these are invalid, but this doesn't
995
concern \.{DVItoMP} because it does not do extensive error checking.
996
The width of character~|c| in font~|f| is given by
997
|char_width(f,c)=width[info_base[f]+c]|, and |info_ptr| is the
998
first unused position of the |width| array.
1000
If font~|f| is a virtual font, there is a list of \.{DVI} commands for each
1001
character. These occupy consecutive positions in the |cmd_buf| array with
1002
the commands for character~|c| starting at
1003
|start_cmd(f,c)=cmd_ptr[info_base[f]+c]| and ending just before
1004
|start_cmd(f,c+1)|. Font numbers used when interpreting these \.{DVI}
1005
commands occupy positions |fbase[f]| through |ftop[f]-1| in the |font_num|
1006
table and the |internal_num| array gives the corresponding internal font
1007
numbers. If such an internal font number~|i| does not correspond to
1008
some font occuring in the \.{DVI} file, then |font_num[i]| has not been
1009
assigned a meaningful value; this is indicated by |local_only[i]=true|.
1011
If font~|f| is not virtual, then |fbase[f]=0| and |ftop[f]=0|. The |start_cmd|
1012
values are ignored in this case.
1014
@d char_width(A,B) mpx->width[mpx->info_base[(A)]+(B)]
1015
@d start_cmd(A,B) mpx->cmd_ptr[mpx->info_base[(A)]+(B)]
1018
web_integer font_num[(max_fnums+1)]; /* external font numbers */
1019
web_integer internal_num[(max_fnums+1)]; /* internal font numbers */
1020
web_boolean local_only[(max_fnums+1)]; /* |font_num| meaningless? */
1021
char *font_name[(max_fonts+1)]; /* starting positions of external font names */
1022
double font_scaled_size[(max_fonts+1)]; /* scale factors over $2^{20}$ */
1023
double font_design_size[(max_fonts+1)]; /* design sizes over $2^{20}$ */
1024
web_integer font_check_sum[(max_fonts+1)]; /* check sum from the |font_def| */
1025
web_integer font_bc[(max_fonts+1)]; /* beginning characters in fonts */
1026
web_integer font_ec[(max_fonts+1)]; /* ending characters in fonts */
1027
web_integer info_base[(max_fonts+1)]; /* index into |width| and |cmd_ptr| tables */
1028
web_integer width[(max_widths+1)];
1029
/* character widths, in units $2^{-20}$ of design size */
1030
web_integer fbase[(max_fonts+1)]; /* index into |font_num| for local fonts */
1031
web_integer ftop[(max_fonts+1)]; /* |font_num| index where local fonts stop */
1032
web_integer cmd_ptr[(max_widths+1)]; /* starting positions in |cmd_buf| */
1033
unsigned int nfonts; /* the number of known fonts */
1034
unsigned int vf_ptr; /* next |font_num| entry for virtual font font tables */
1035
unsigned int info_ptr; /* allocation pointer for |width| and |cmd_ptr| tables */
1036
unsigned int n_cmds; /* number of occupied cells in |cmd_buf| */
1037
unsigned int cur_fbase, cur_ftop;
1038
/* currently applicable part of the |font_num| table */
1041
mpx->nfonts=0; mpx->info_ptr=0; mpx->font_name[0]=0;
1042
mpx->vf_ptr=max_fnums;
1043
mpx->cur_fbase=0; mpx->cur_ftop=0;
1045
@ Printing the name of a given font is easy except that a procedure |print_char|
1046
is needed to actually send an |ASCII_code| to the \.{MPX} file.
1048
@c @<Declare subroutines for printing strings@>@;
1049
static void mpx_print_font (MPX mpx, web_integer f) { /* |f| is an internal font number */
1050
if ( (f<0)||(f>=(int)mpx->nfonts) ) {
1051
bad_dvi("Undefined font");
1053
char *s = mpx->font_name[f];
1055
mpx_print_char(mpx,(unsigned char)*s);
1061
@ Sometimes a font name is needed as part of an error message.
1063
@d font_warn(A,B) mpx_warn (mpx,"%s %s",A,mpx->font_name[(B)])
1064
@d font_error(A,B) mpx_error(mpx,"%s %s",A,mpx->font_name[(B)])
1065
@d font_abort(A,B) mpx_abort(mpx,"%s %s",A,mpx->font_name[(B)])
1068
@ When we encounter a font definition, we save the name, checksum, and size
1069
information, but we don't actually read the \.{TFM} or \.{VF} file until we
1070
are about to use the font. If a matching font is not already defined, we then
1071
allocate a new internal font number.
1073
The following subroutine does the necessary things when a \\{fnt\_def} command
1074
is encountered in the \.{DVI} file or in a \.{VF} file. It assumes that the
1075
first argument has already been parsed and is given by the parameter~|e|.
1077
@c @<Declare a function called |match_font|@>@;
1078
static void mpx_define_font (MPX mpx, web_integer e) { /* |e| is an external font number */
1079
unsigned i; /* index into |font_num| and |internal_num| */
1080
web_integer n; /* length of the font name and area */
1081
web_integer k; /* general purpose loop counter */
1082
web_integer x; /* a temporary value for scaled size computation */
1083
if ( mpx->nfonts==max_fonts )
1084
mpx_abort(mpx,"DVItoMP capacity exceeded (max fonts=%d)!", max_fonts);
1085
@.DVItoMP capacity exceeded...@>
1086
@<Allocate an index |i| into the |font_num| and |internal_num| tables@>;
1087
@<Read the font parameters into position for font |nf|@>;
1088
mpx->internal_num[i]=mpx_match_font(mpx, mpx->nfonts,true);
1089
if ( mpx->internal_num[i]==(int)mpx->nfonts ) {
1090
mpx->info_base[mpx->nfonts]=max_widths; /* indicate that the info isn't loaded yet */
1091
mpx->local_only[mpx->nfonts]=mpx->vf_reading; incr(mpx->nfonts);
1095
@ @<Allocate an index |i| into the |font_num| and |internal_num| tables@>=
1096
if ( mpx->vf_ptr==mpx->nfonts )
1097
mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums);
1098
@.DVItoMP capacity exceeded...@>
1099
if ( mpx->vf_reading ) {
1100
mpx->font_num[mpx->nfonts]=0; i=mpx->vf_ptr; decr(mpx->vf_ptr);
1106
@ @<Read the font parameters into position for font |nf|@>=
1107
mpx->font_check_sum[mpx->nfonts]=mpx_signed_quad(mpx);
1108
@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>;
1109
n=mpx_get_byte(mpx); /* that is the area */
1110
n=n+mpx_get_byte(mpx);
1111
mpx->font_name[mpx->nfonts]=xmalloc((size_t)(n+1),1);
1113
mpx->font_name[mpx->nfonts][k]=(char)mpx_get_byte(mpx);
1114
mpx->font_name[mpx->nfonts][k]=0
1116
@ The scaled size and design size are stored in \.{DVI} units divided by $2^{20}$.
1117
The units for scaled size are a little different if we are reading a virtual
1118
font, but this will be corrected when the scaled size is used. The scaled size
1119
also needs to be truncated to at most 23 significant bits in order to make
1120
the character width calculation match what \TeX\ does.
1122
@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>=
1123
x=mpx_signed_quad(mpx);
1125
while ( mpx->x>040000000 ) {
1128
mpx->font_scaled_size[mpx->nfonts]=x*k/1048576.0;
1129
if ( mpx->vf_reading )
1130
mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)*mpx->dvi_per_fix/1048576.0;
1131
else mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)/1048576.0;
1134
double dvi_per_fix; /* converts points scaled $2^{20}$ to \.{DVI} units */
1136
@ The |match_font| function tries to find a match for the font with internal
1137
number~|ff|, returning |nf| or the number of the matching font. If
1138
|exact=true|, the name and scaled size should match. Otherwise the scaled
1139
size need not match but the font found must be already loaded, not just
1142
@<Declare a function called |match_font|@>=
1143
static web_integer mpx_match_font (MPX mpx, unsigned ff, web_boolean exact) {
1144
unsigned f; /* font number being tested */
1145
for (f=0; f<mpx->nfonts ; f++) {
1147
@<Compare the names of fonts |f| and |ff|; |continue| if they differ@>;
1149
if ( fabs(mpx->font_scaled_size[f]-mpx->font_scaled_size[ff])<= font_tolerance ) {
1150
if ( ! mpx->vf_reading ) {
1151
if ( mpx->local_only[f] ) {
1152
mpx->font_num[f]=mpx->font_num[ff]; mpx->local_only[f]=false;
1153
} else if ( mpx->font_num[f]!=mpx->font_num[ff] ) {
1159
} else if ( mpx->info_base[f]!=max_widths ) {
1164
if ( f<mpx->nfonts ) {
1165
@<Make sure fonts |f| and |ff| have matching design sizes and checksums@>;
1167
return (web_integer)f;
1170
@ @<Compare the names of fonts |f| and |ff|; |continue| if they differ@>=
1171
if (strcmp(mpx->font_name[f],mpx->font_name[ff]))
1174
@ @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>=
1175
if ( fabs(mpx->font_design_size[f]-mpx->font_design_size[ff]) > font_tolerance ) {
1176
font_error("Inconsistent design sizes given for ",ff);
1177
@.Inconsistent design sizes@>
1178
} else if ( mpx->font_check_sum[f]!=mpx->font_check_sum[ff] ) {
1179
font_warn("Checksum mismatch for ", ff);
1180
@.Checksum mismatch@>
1183
@* Reading ordinary fonts.
1184
An auxiliary array |in_width| is used to hold the widths as they are
1185
input. The global variable |tfm_check_sum| is set to the check sum that
1186
appears in the current \.{TFM} file.
1189
web_integer in_width[256]; /* \.{TFM} width data in \.{DVI} units */
1190
web_integer tfm_check_sum; /* check sum found in |tfm_file| */
1192
@ Here is a procedure that absorbs the necessary information from a
1193
\.{TFM} file, assuming that the file has just been successfully reset
1194
so that we are ready to read its first byte. (A complete description of
1195
\.{TFM} file format appears in the documentation of \.{TFtoPL} and will
1196
not be repeated here.) The procedure does not check the \.{TFM} file
1197
for validity, nor does it give explicit information about what is
1198
wrong with a \.{TFM} file that proves to be invalid. The procedure simply
1199
aborts the program if it detects anything amiss in the \.{TFM} data.
1202
static void mpx_in_TFM (MPX mpx,web_integer f) {
1203
/* input \.{TFM} data for font |f| or abort */
1204
web_integer k; /* index for loops */
1205
int lh; /* length of the header data, in four-byte words */
1206
int nw; /* number of words in the width table */
1207
unsigned int wp; /* new value of |info_ptr| after successful input */
1208
@<Read past the header data; |abort| if there is a problem@>;
1209
@<Store character-width indices at the end of the |width| table@>;
1210
@<Read the width values into the |in_width| table@>;
1211
@<Move the widths from |in_width| to |width|@>;
1212
mpx->fbase[f]=0; mpx->ftop[f]=0;
1214
mpx_fclose(mpx,mpx->tfm_file);
1218
@ @<Read past the header...@>=
1219
mpx_read_tfm_word(mpx); lh=mpx->b2*(int)(256)+mpx->b3;
1220
mpx_read_tfm_word(mpx);
1221
mpx->font_bc[f]=mpx->b0*(int)(256)+mpx->b1;
1222
mpx->font_ec[f]=mpx->b2*(int)(256)+mpx->b3;
1223
if ( mpx->font_ec[f]<mpx->font_bc[f] ) mpx->font_bc[f]=mpx->font_ec[f]+1;
1224
if ( mpx->info_ptr+(unsigned int)mpx->font_ec[f]-(unsigned int)mpx->font_bc[f]+1>max_widths )
1225
mpx_abort(mpx,"DVItoMP capacity exceeded (width table size=%d)!",max_widths);
1226
@.DVItoMP capacity exceeded...@>
1227
wp=mpx->info_ptr+(unsigned int)mpx->font_ec[f]-(unsigned int)mpx->font_bc[f]+1;
1228
mpx_read_tfm_word(mpx); nw=mpx->b0*256+mpx->b1;
1229
if ( (nw==0)||(nw>256) )
1230
font_abort("Bad TFM file for ",f);
1232
for (k=1;k<=3+lh;k++) {
1233
if ( feof(mpx->tfm_file) )
1234
font_abort("Bad TFM file for ",f);
1236
mpx_read_tfm_word(mpx);
1239
mpx->tfm_check_sum=((mpx->b0*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
1241
mpx->tfm_check_sum=(((mpx->b0-256)*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
1244
if (mpx->mode == mpx_troff_mode) {
1245
mpx->font_design_size[f]=(((mpx->b0*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3)/(65536.0*16);
1250
@ @<Store character-width indices...@>=
1252
for (k=(int)mpx->info_ptr;k<=(int)wp-1;k++ ) {
1253
mpx_read_tfm_word(mpx);
1255
font_abort("Bad TFM file for ",f);
1257
mpx->width[k]=mpx->b0;
1261
@ No fancy width calculation is needed here because \.{DVItoMP} stores
1262
widths in their raw form as multiples of the design size scaled by $2^{20}$.
1263
The |font_scaled_size| entries have been computed so that the final width
1264
compution can be done in floating point if enough precision is available.
1266
@<Read the width values into the |in_width| table@>=
1267
for (k=0;k<=nw-1;k++) {
1268
mpx_read_tfm_word(mpx);
1269
if ( mpx->b0>127 ) mpx->b0=mpx->b0-256;
1270
mpx->in_width[k]=((mpx->b0*0400+mpx->b1)*0400+mpx->b2)*0400+mpx->b3;
1273
@ The width compution uses a scale factor |dvi_scale| that will be introduced
1274
later. It is equal to one when not typesetting a character from a virtual
1275
font. In that case, the following expressions do the width computation that is
1276
so important in \.{DVItype}. It is less important here because it is impractical
1277
to guarantee precise character positioning in \MP\ output. Nevertheless, the
1278
width compution will be precise if reals have at least 46-bit mantissas and
1279
|round(x-.5)| is equivalent to $\lfloor x\rfloor$. It may be a good idea to
1280
modify this computation if these conditions are not met.
1281
@^system dependencies@>
1283
@<Width of character |c| in font |f|@>=
1284
floor(mpx->dvi_scale*mpx->font_scaled_size[f]*char_width(f,c))
1286
@ @<Width of character |p| in font |cur_font|@>=
1287
floor(mpx->dvi_scale*mpx->font_scaled_size[cur_font]*char_width(cur_font,p))
1289
@ @<Move the widths from |in_width| to |width|@>=
1290
if ( mpx->in_width[0]!=0 )
1291
font_abort("Bad TFM file for ",f); /* the first width should be zero */
1293
mpx->info_base[f]=(int)(mpx->info_ptr-(unsigned int)mpx->font_bc[f]);
1295
for (k=(int)mpx->info_ptr;k<=(int)wp-1;k++) {
1296
mpx->width[k]=mpx->in_width[mpx->width[k]];
1301
@* Reading virtual fonts.
1303
The |in_VF| procedure absorbs the necessary information from a \.{VF} file that
1304
has just been reset so that we are ready to read the first byte. (A complete
1305
description of \.{VF} file format appears in the documention of \.{VFtoVP}).
1306
Like |in_TFM|, this procedure simply aborts the program if it detects anything
1307
wrong with the \.{VF} file.
1310
@<Declare a function called |first_par|@>@;
1311
static void mpx_in_VF (MPX mpx, web_integer f) {
1312
/* read \.{VF} data for font |f| or abort */
1313
web_integer p; /* a byte from the \.{VF} file */
1314
boolean was_vf_reading; /* old value of |vf_reading| */
1315
web_integer c; /* the current character code */
1316
web_integer limit; /* space limitations force character codes to be less than this */
1317
web_integer w; /* a \.{TFM} width being read */
1318
was_vf_reading=mpx->vf_reading; mpx->vf_reading=true;
1319
@<Start reading the preamble from a \.{VF} file@>;@/
1320
@<Initialize the data structures for the virtual font@>;@/
1321
p=mpx_get_byte(mpx);
1322
while ( p>=fnt_def1 ) {
1324
font_abort("Bad VF file for ",f);
1325
mpx_define_font(mpx, mpx_first_par(mpx, (unsigned int)p));
1326
p=mpx_get_byte(mpx);
1329
if ( feof(mpx->vf_file) )
1330
font_abort("Bad VF file for ",f);
1331
@<Read the packet length, character code, and \.{TFM} width@>;
1332
@<Store the character packet in |cmd_buf|@>;
1333
p=mpx_get_byte(mpx);
1336
@<Finish setting up the data structures for the new virtual font@>;
1337
mpx->vf_reading=was_vf_reading;
1342
@ @<Start reading the preamble from a \.{VF} file@>=
1343
p=mpx_get_byte(mpx);
1345
font_abort("Bad VF file for ",f);
1346
p=mpx_get_byte(mpx); /* fetch the identification byte */
1348
font_abort("Bad VF file for ",f);
1349
p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */
1351
(void)mpx_get_byte(mpx);
1352
mpx->tfm_check_sum=mpx_signed_quad(mpx);
1353
(void)mpx_signed_quad(mpx); /* skip over the design size */
1355
@ @<Initialize the data structures for the virtual font@>=
1356
mpx->ftop[f]=(web_integer)mpx->vf_ptr;
1357
if ( mpx->vf_ptr==mpx->nfonts )
1358
mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums);
1359
@.DVItoMP capacity exceeded...@>
1361
mpx->info_base[f]=(web_integer)mpx->info_ptr;
1362
limit=max_widths-mpx->info_base[f];@/
1363
mpx->font_bc[f]=limit; mpx->font_ec[f]=0
1365
@ @<Read the packet length, character code, and \.{TFM} width@>=
1367
p=mpx_signed_quad(mpx); c=mpx_signed_quad(mpx); w=mpx_signed_quad(mpx);
1369
font_abort("Bad VF file for ",f);
1371
c=mpx_get_byte(mpx); w=mpx_get_three_bytes(mpx);
1374
mpx_abort(mpx,"DVItoMP capacity exceeded (max widths=%d)", max_widths);
1375
@.DVItoMP capacity exceeded...@>
1376
if ( c<mpx->font_bc[f] ) mpx->font_bc[f]=c;
1377
if ( c>mpx->font_ec[f] ) mpx->font_ec[f]=c;
1380
@ @<Store the character packet in |cmd_buf|@>=
1381
if ( mpx->n_cmds+(unsigned int)p>=virtual_space )
1382
mpx_abort(mpx,"DVItoMP capacity exceeded (virtual font space=%d)",virtual_space);
1383
@.DVItoMP capacity exceeded...@>
1384
start_cmd(f,c)=(web_integer)mpx->n_cmds;
1386
mpx->cmd_buf[mpx->n_cmds]=(unsigned char)mpx_get_byte(mpx);
1387
incr(mpx->n_cmds); decr(p);
1389
mpx->cmd_buf[mpx->n_cmds]=eop; /* add the end-of-packet marker */
1392
@ There are unused |width| and |cmd_ptr| entries if |font_bc[f]>0| but it isn't
1393
worthwhile to slide everything down just to save a little space.
1395
@<Finish setting up the data structures for the new virtual font@>=
1396
mpx->fbase[f]=(web_integer)(mpx->vf_ptr+1);
1397
mpx->info_ptr=(unsigned int)(mpx->info_base[f]+mpx->font_ec[f]+1)
1402
The character width information for a font is loaded when the font is selected
1403
for the first time. This information might already be loaded if the font has
1404
already been used at a different scale factor. Otherwise, we look for a \.{VF}
1405
file, or failing that, a \.{TFM} file. All this is done by the |select_font|
1406
function that takes an external font number~|e| and returns the corresponding
1407
internal font number with the width information loaded.
1410
static web_integer mpx_select_font (MPX mpx, web_integer e) {
1411
int f; /* the internal font number */
1412
int ff; /* internal font number for an existing version */
1413
web_integer k; /* general purpose loop counter */
1414
@<Set |f| to the internal font number that corresponds to |e|,
1415
or |abort| if there is none@>;
1416
if ( mpx->info_base[f]==max_widths ) {
1417
ff=mpx_match_font(mpx, (unsigned)f,false);
1418
if ( ff<(int)mpx->nfonts ) {
1419
@<Make font |f| refer to the width information from font |ff|@>;
1421
@<Move the \.{VF} file name into the |cur_name| string@>;
1422
if ( mpx_open_vf_file(mpx) ) {
1425
if ( ! mpx_open_tfm_file(mpx) )
1426
font_abort("No TFM file found for ",f);
1427
@.no TFM file found@>
1430
@<Make sure the checksum in the font file matches the one given in the
1431
|font_def| for font |f|@>;
1433
@<Do any other initialization required for the new font |f|@>;
1438
@ @<Set |f| to the internal font number that corresponds to |e|,...@>=
1439
if ( mpx->cur_ftop<=mpx->nfonts )
1440
mpx->cur_ftop=mpx->nfonts;
1441
mpx->font_num[mpx->cur_ftop]=e;
1442
k=(web_integer)mpx->cur_fbase;
1443
while ((mpx->font_num[k]!=e)|| mpx->local_only[k] ) incr(k);
1444
if ( k==(int)mpx->cur_ftop )
1445
mpx_abort(mpx,"Undefined font selected");
1446
f=mpx->internal_num[k]
1448
@ @<Make font |f| refer to the width information from font |ff|@>=
1450
mpx->font_bc[f]=mpx->font_bc[ff];
1451
mpx->font_ec[f]=mpx->font_ec[ff];
1452
mpx->info_base[f]=mpx->info_base[ff];
1453
mpx->fbase[f]=mpx->fbase[ff];
1454
mpx->ftop[f]=mpx->ftop[ff];
1457
@ The string |cur_name| is supposed to be set to the external name of the
1458
\.{VF} file for the current font.
1459
@^system dependencies@>
1461
@<Move the \.{VF} file name into the |cur_name| string@>=
1462
mpx->cur_name = xstrdup (mpx->font_name[f])
1464
@ @<Make sure the checksum in the font file matches the one given in the...@>=
1466
if ( (mpx->font_check_sum[f]!=0)&&(mpx->tfm_check_sum!=0)&&@|
1467
(mpx->font_check_sum[f]!=mpx->tfm_check_sum) ) {
1468
font_warn("Checksum mismatch for ",f);
1469
@.Checksum mismatch@>
1473
@* Low level output routines.
1475
One of the basic output operations is to write a \MP\ string expression for
1476
a sequence of characters to be typeset. The main difficulties are that such
1477
strings can contain arbitrary eight-bit bytes and there is no fixed limit on
1478
the length of the string that needs to be produced. In extreme cases this
1479
can lead to expressions such as
1481
\hbox{\.{char7\&char15\&char31\&"?FWayzz"}}
1482
\hbox{\.{\&"zzaF"\&char15\&char3\&char31}}
1483
\hbox{\.{\&"Nxzzzzzzzwvtsqo"}}}
1486
@ A global variable |state| keeps track of the output process.
1487
When |state=normal| we have begun a quoted string and the next character
1488
should be a printable character or a closing quote. When |state=special|
1489
the last thing printed was a ``\.{char}'' construction or a closing quote
1490
and an ampersand should come next. The starting condition |state=initial|
1491
is a lot like |state=special|, except no ampersand is required.
1493
@d special 0 /* the |state| after printing a ``\.{char}'' expression */
1494
@d normal 1 /* the |state| value in a quoted string */
1495
@d initial 2 /* initial |state| */
1498
int state; /* controls the process of printing a string */
1499
int print_col; /* there are at most this many characters on the current line */
1501
@ @<Set initial values@>=
1502
mpx->state = initial;
1503
mpx->print_col = 0; /* there are at most this many characters on the current line */
1505
@ To print a string on the \.{MPX} file, initialize |print_col|, ensure that
1506
|state=initial|, and pass the characters one-at-a-time to |print_char|.
1508
@<Declare subroutines for printing strings@>=
1509
static void mpx_print_char (MPX mpx, unsigned char c) {
1510
web_integer l; /* number of characters to print |c| or the \.{char} expression */
1511
if ( printable(c) ) l=1;
1512
else if ( c<10 ) l=5;
1513
else if ( c<100 ) l=6;
1515
if ( mpx->print_col+l>line_length-2 ) {
1516
if ( mpx->state==normal ) {
1517
fprintf(mpx->mpxfile,"\""); mpx->state=special;
1519
fprintf(mpx->mpxfile,"\n");
1522
@<Print |c| and update |state| and |print_col|@>;
1525
@ @<Print |c| and update |state| and |print_col|@>=
1526
if ( mpx->state==normal ) {
1527
if ( printable(c) ) {
1528
fprintf(mpx->mpxfile,"%c",xchr(c));
1530
fprintf(mpx->mpxfile,"\"&char%d",c);
1534
if ( mpx->state==special ) {
1535
fprintf(mpx->mpxfile,"&");
1536
incr(mpx->print_col);
1538
if ( printable(c) ) {
1539
fprintf(mpx->mpxfile,"\"%c",xchr(c));
1540
incr(mpx->print_col);
1542
fprintf(mpx->mpxfile,"char%d",c);
1545
mpx->print_col += l;
1551
@ The |end_char_string| procedure gets the string ended properly and ensures
1552
that there is room for |l| more characters on the output line.
1554
@<Declare subroutines for printing strings@>=
1555
static void mpx_end_char_string (MPX mpx,web_integer l) {
1556
while ( mpx->state>special ){
1557
fprintf(mpx->mpxfile,"\"");
1558
incr(mpx->print_col);
1561
if ( mpx->print_col+l>line_length ) {
1562
fprintf(mpx->mpxfile,"\n "); mpx->print_col=0;
1564
mpx->state=initial; /* get ready to print the next string */
1567
@ Since |end_char_string| resets |state:=initial|, all we have to do is set
1568
|state:=initial| once at the beginning.
1573
@ Characters and rules are positioned according to global variables |h| and~|v|
1574
as will be explained later. We also need scale factors that convert quantities
1575
to the right units when they are printed in the \.{MPX} file.
1577
Even though all variable names in the \MP\ output are made local via \.{save}
1578
commands, it is still desirable to preceed them with underscores. This makes
1579
the output more likely to work when used in a macro definition, since the
1580
generated variables names must not collide with formal parameters in such
1585
web_integer v; /* the current position in \.{DVI} units */
1586
double conv; /* converts \.{DVI} units to \MP\ points */
1587
double mag; /* magnification factor times 1000 */
1589
@ @c @<Declare a procedure called |finish_last_char|@>@;
1590
static void mpx_do_set_char (MPX mpx,web_integer f, web_integer c) {
1591
if ( (c<mpx->font_bc[f])||(c>mpx->font_ec[f]) )
1592
mpx_abort(mpx,"attempt to typeset invalid character %d",c);
1593
@.attempt to typeset...@>
1594
if ((mpx->h!=mpx->str_h2)||(mpx->v!=mpx->str_v)||
1595
(f!=mpx->str_f)||(mpx->dvi_scale!=mpx->str_scale) ) {
1596
if ( mpx->str_f>=0 ) {
1597
mpx_finish_last_char(mpx);
1598
} else if ( ! mpx->fonts_used ) {
1599
@<Prepare to output the first character on a page@>;
1601
if ( ! mpx->font_used[f] )
1602
@<Prepare to use font |f| for the first time on a page@>;
1603
fprintf(mpx->mpxfile,"_s("); mpx->print_col=3;@/
1604
mpx->str_scale=mpx->dvi_scale; mpx->str_f=f;
1605
mpx->str_v=mpx->v; mpx->str_h1=mpx->h;
1607
mpx_print_char(mpx, (unsigned char)c);
1608
mpx->str_h2=(web_integer)(mpx->h+@<Width of character |c| in font |f|@>);
1612
boolean font_used[(max_fonts+1)]; /* has this font been used on this page? */
1613
boolean fonts_used; /* has any font been used on this page? */
1614
boolean rules_used; /* has any rules been set on this page? */
1616
web_integer str_v; /* starting position for current output string */
1617
web_integer str_h2; /* where the current output string ends */
1618
web_integer str_f; /* internal font number for the current output string */
1619
double str_scale; /* value of |dvi_scale| for the current output string */
1622
@ Before using any fonts we need to define a MetaPost macro for
1623
typesetting character strings. The |font_used| array is not
1624
initialized until it is actually time to output a character.
1627
static void mpx_prepare_font_use(MPX mpx);
1630
static void mpx_prepare_font_use(MPX mpx) {
1632
for (k=0; k<mpx->nfonts;k++ )
1633
mpx->font_used[k]=false;
1634
mpx->fonts_used=true;
1635
fprintf(mpx->mpxfile,"string _n[];\n");
1636
fprintf(mpx->mpxfile,"vardef _s(expr _t,_f,_m,_x,_y)(text _c)=\n");
1637
fprintf(mpx->mpxfile,
1638
" addto _p also _t infont _f scaled _m shifted (_x,_y) _c; enddef;\n");
1641
@ @<Prepare to output the first character on a page@>=
1642
mpx_prepare_font_use(mpx)
1645
@ @<Do any other initialization required for the new font |f|@>=
1646
mpx->font_used[f]=false;
1648
@ Do what is necessary when the font with internal number f is used for the
1649
first time on a page.
1652
static void mpx_first_use(MPX mpx, int f) ;
1655
static void mpx_first_use(MPX mpx, int f) {
1656
mpx->font_used[f]=true;
1657
fprintf(mpx->mpxfile,"_n%d=",f);
1659
mpx_print_font(mpx, f);
1660
mpx_end_char_string(mpx, 1);
1661
fprintf(mpx->mpxfile,";\n");
1664
@ @<Prepare to use font |f| for the first time on a page@>=
1665
mpx_first_use(mpx,f);
1667
@ We maintain the invariant that |str_f=-1| when there is no output string
1670
@<Declare a procedure called |finish_last_char|@>=
1671
static void mpx_finish_last_char (MPX mpx) {
1673
/* font scale factor and \MP\ coordinates of reference point */
1674
if ( mpx->str_f>=0 ) {
1675
if (mpx->mode==mpx_tex_mode) {
1676
m=mpx->str_scale*mpx->font_scaled_size[mpx->str_f]*
1677
mpx->mag/mpx->font_design_size[mpx->str_f];
1678
x=mpx->conv*mpx->str_h1;
1679
y=mpx->conv*(-mpx->str_v);
1680
if ( (fabs(x)>=4096.0)||(fabs(y)>=4096.0)||(m>=4096.0)||(m<0) ) {
1681
mpx_warn(mpx,"text is out of range");
1682
mpx_end_char_string(mpx, 60);
1684
mpx_end_char_string(mpx, 40);
1686
fprintf(mpx->mpxfile,",_n%d,%1.5f,%1.4f,%1.4f,",mpx->str_f,m,x,y);
1687
@<Print a \.{withcolor} specifier if appropriate@>@/
1688
fprintf(mpx->mpxfile,");\n");
1690
m = mpx->str_size / mpx->font_design_size[mpx->str_f];
1691
x = mpx->dmp_str_h1 * mpx->unit;
1692
y = YCORR - mpx->dmp_str_v * mpx->unit;
1693
if (fabs(x) >= 4096.0 || fabs(y) >= 4096.0 || m >= 4096.0 || m < 0) {
1694
mpx_warn(mpx,"text out of range ignored");
1695
mpx_end_char_string(mpx,67);
1697
mpx_end_char_string(mpx,47);
1699
fprintf(mpx->mpxfile, "), _n%d", mpx->str_f);
1700
fprintf(mpx->mpxfile, ",%.5f,%.4f,%.4f)", (m*1.00375), (x/100.0), y);
1701
mpx_slant_and_ht(mpx);
1702
fprintf(mpx->mpxfile, ";\n");
1708
@ Setting rules is fairly simple.
1711
static void mpx_do_set_rule (MPX mpx,web_integer ht, web_integer wd) {
1712
double xx1,yy1,xx2,yy2,ww;
1713
/* \MP\ coordinates of lower-left and upper-right corners */
1715
@<Handle a special rule that determines the box size@>
1716
} else if ( (ht>0)||(wd>0) ) {
1717
if ( mpx->str_f>=0 )
1718
mpx_finish_last_char(mpx);
1719
if ( ! mpx->rules_used ) {
1720
mpx->rules_used=true;
1721
fprintf(mpx->mpxfile,
1722
"interim linecap:=0;\n"
1723
"vardef _r(expr _a,_w)(text _t) =\n"
1724
" addto _p doublepath _a withpen pencircle scaled _w _t enddef;");
1726
@<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke
1727
and |ww| the desired stroke width@>;
1728
if ( (fabs(xx1)>=4096.0)||(fabs(yy1)>=4096.0)||@|
1729
(fabs(xx2)>=4096.0)||(fabs(yy2)>=4096.0)||(ww>=4096.0) )
1730
mpx_warn(mpx,"hrule or vrule is out of range");
1731
fprintf(mpx->mpxfile,"_r((%1.4f,%1.4f)..(%1.4f,%1.4f), %1.4f,",xx1,yy1,xx2,yy2,ww);
1732
@<Print a \.{withcolor} specifier if appropriate@>@/
1733
fprintf(mpx->mpxfile,");\n");
1737
@ @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke...@>=
1738
xx1=mpx->conv*mpx->h;
1739
yy1=mpx->conv*(-mpx->v);
1741
xx2=xx1+mpx->conv*wd;
1746
yy2=yy1+mpx->conv*ht;
1752
@ Rules of width one dvi unit are not typeset since \.{MPtoTeX} adds an
1753
extraneous rule of this width in order to allow \.{DVItoMP} to deduce the
1754
dimensions of the boxes it ships out. The box width is the left edge of the
1755
last such rule; the height and depth are at the top and bottom of the rule.
1756
There should be only one special rule per picture but there could be more if
1757
the user tries to typeset his own one-dvi-unit rules. In this case the
1758
dimension-determining rule is the last one in the picture.
1760
@<Handle a special rule that determines the box size@>=
1762
mpx->pic_wd=mpx->h; mpx->pic_dp=mpx->v; mpx->pic_ht=ht-mpx->v;
1766
web_integer pic_dp; web_integer pic_ht; web_integer pic_wd; /* picture dimensions from special rule */
1768
@ The following initialization and clean-up is required. We do a little more
1769
initialization than is absolutely necessary since some compilers might complain
1770
if the variables are uninitialized when |do_set_char| tests them.
1773
static void mpx_start_picture (MPX mpx) {
1774
mpx->fonts_used=false;
1775
mpx->rules_used=false;
1776
mpx->graphics_used=false;
1780
mpx->str_scale=1.0; /* values don't matter */
1781
mpx->dmp_str_v = 0.0;
1782
mpx->dmp_str_h2 = 0.0;
1783
mpx->str_size = 0.0;
1784
fprintf(mpx->mpxfile,
1785
"begingroup save %s_p,_r,_s,_n; picture _p; _p=nullpicture;\n",
1786
(mpx->mode == mpx_tex_mode ? "" : "_C,_D,"));
1789
static void mpx_stop_picture (MPX mpx) {
1790
double w,h,dd; /* width, height, negative depth in PostScript points */
1791
if ( mpx->str_f>=0 )
1792
mpx_finish_last_char(mpx);
1793
if (mpx->mode==mpx_tex_mode) {
1794
@<Print a \&{setbounds} command based on picture dimensions@>;
1796
fprintf(mpx->mpxfile,"_p endgroup\n");
1799
@ @<Print a \&{setbounds} command based on picture dimensions@>=
1800
dd=-mpx->pic_dp*mpx->conv;
1801
w=mpx->conv*mpx->pic_wd;
1802
h=mpx->conv*mpx->pic_ht;
1803
fprintf(mpx->mpxfile,
1804
"setbounds _p to (0,%1.4f)--(%1.4f,%1.4f)--\n"
1805
" (%1.4f,%1.4f)--(0,%1.4f)--cycle;\n",dd,w,dd,w,h,h)
1807
@* Translation to symbolic form.
1809
The main work of \.{DVItoMP} is accomplished by the |do_dvi_commands|
1810
procedure, which produces the output for an entire page, assuming that the
1811
|bop| command for that page has already been processed. This procedure is
1812
essentially an interpretive routine that reads and acts on the \.{DVI}
1813
commands. It is also capable of executing the typesetting commands for
1814
a character in a virtual font.
1816
@ The definition of \.{DVI} files refers to six registers,
1817
$(h,v,w,x,y,z)$, which hold web_integer values in \.{DVI} units.
1818
These units come directly from the input file except they need to be
1819
rescaled when typesetting characters from a virtual font.
1820
The stack of $(h,v,w,x,y,z)$ values is represented by six arrays
1821
called |hstack|, \dots, |zstack|.
1824
web_integer w;web_integer x;web_integer y;web_integer z;
1825
/* current state values (|h| and |v| have already been declared) */
1826
web_integer hstack[(stack_size+1)];
1827
web_integer vstack[(stack_size+1)];
1828
web_integer wstack[(stack_size+1)];
1829
web_integer xstack[(stack_size+1)];
1830
web_integer ystack[(stack_size+1)];
1831
web_integer zstack[(stack_size+1)]; /* pushed down values in \.{DVI} units */
1832
web_integer stk_siz; /* the current stack size */
1833
double dvi_scale; /* converts units of current input source to \.{DVI} units */
1835
@ @<Do initialization required before starting a new page@>=
1839
mpx->Xslant = 0.0; mpx->Xheight = 0.0
1841
@ Next, we need procedures to handle |push| and |pop| commands.
1843
@c @<Declare procedures to handle color commands@>
1844
static void mpx_do_push (MPX mpx) {
1845
if ( mpx->stk_siz==stack_size )
1846
mpx_abort(mpx,"DVItoMP capacity exceeded (stack size=%d)",stack_size);
1847
@.DVItoMP capacity exceeded...@>
1848
mpx->hstack[mpx->stk_siz]=mpx->h;
1849
mpx->vstack[mpx->stk_siz]=mpx->v; mpx->wstack[mpx->stk_siz]=mpx->w;
1850
mpx->xstack[mpx->stk_siz]=mpx->x;
1851
mpx->ystack[mpx->stk_siz]=mpx->y; mpx->zstack[mpx->stk_siz]=mpx->z;
1855
static void mpx_do_pop (MPX mpx) {
1856
if ( mpx->stk_siz==0 )
1857
bad_dvi("attempt to pop empty stack");
1860
mpx->h=mpx->hstack[mpx->stk_siz];
1861
mpx->v=mpx->vstack[mpx->stk_siz]; mpx->w=mpx->wstack[mpx->stk_siz];
1862
mpx->x=mpx->xstack[mpx->stk_siz];
1863
mpx->y=mpx->ystack[mpx->stk_siz]; mpx->z=mpx->zstack[mpx->stk_siz];
1867
@ The |set_virtual_char| procedure is mutually recursive with
1868
|do_dvi_commands|. This is really a supervisory
1870
procedure that calls |do_set_char| or adjusts the input source to read
1871
typesetting commands for a character in a virtual font.
1874
static void mpx_do_dvi_commands (MPX mpx);
1875
static void mpx_set_virtual_char (MPX mpx,web_integer f, web_integer c) {
1876
double old_scale; /* original value of |dvi_scale| */
1877
unsigned old_buf_ptr; /* original value of the input pointer |buf_ptr| */
1878
unsigned old_fbase,old_ftop; /* originally applicable part of the |font_num| table */
1879
if ( mpx->fbase[f]==0 )
1880
mpx_do_set_char(mpx, f,c);
1882
old_fbase=mpx->cur_fbase; old_ftop=mpx->cur_ftop;
1883
mpx->cur_fbase=(unsigned int)mpx->fbase[f];
1884
mpx->cur_ftop=(unsigned int)mpx->ftop[f];
1885
old_scale=mpx->dvi_scale;
1886
mpx->dvi_scale=mpx->dvi_scale*mpx->font_scaled_size[f];
1887
old_buf_ptr=mpx->buf_ptr;
1888
mpx->buf_ptr=(unsigned int)start_cmd(f,c);
1890
mpx_do_dvi_commands(mpx);
1892
mpx->buf_ptr=old_buf_ptr;
1893
mpx->dvi_scale=old_scale;
1894
mpx->cur_fbase=old_fbase;
1895
mpx->cur_ftop=old_ftop;
1899
@ Before we get into the details of |do_dvi_commands|, it is convenient to
1900
consider a simpler routine that computes the first parameter of each
1903
@d four_cases(A) (A): case (A)+1: case (A)+2: case (A)+3
1904
@d eight_cases(A) four_cases((A)): case four_cases((A)+4)
1905
@d sixteen_cases(A) eight_cases((A)): case eight_cases((A)+8)
1906
@d thirty_two_cases(A) sixteen_cases((A)): case sixteen_cases((A)+16)
1907
@d sixty_four_cases(A) thirty_two_cases((A)): case thirty_two_cases((A)+32)
1909
@<Declare a function called |first_par|@>=
1910
static web_integer mpx_first_par (MPX mpx, unsigned int o) {
1912
case sixty_four_cases(set_char_0):
1913
case sixty_four_cases(set_char_0+64):
1914
return (web_integer)(o-set_char_0);
1916
case set1: case put1: case fnt1: case xxx1: case fnt_def1:
1917
return mpx_get_byte(mpx);
1919
case set1+1: case put1+1: case fnt1+1: case xxx1+1: case fnt_def1+1:
1920
return mpx_get_two_bytes(mpx);
1922
case set1+2: case put1+2: case fnt1+2: case xxx1+2: case fnt_def1+2:
1923
return mpx_get_three_bytes(mpx);
1925
case right1: case w1: case x1: case down1: case y1: case z1:
1926
return mpx_signed_byte(mpx);
1928
case right1+1: case w1+1: case x1+1: case down1+1: case y1+1: case z1+1:
1929
return mpx_signed_pair(mpx);
1931
case right1+2: case w1+2: case x1+2: case down1+2: case y1+2: case z1+2:
1932
return mpx_signed_trio(mpx);
1934
case set1+3: case set_rule: case put1+3: case put_rule:
1935
case right1+3: case w1+3: case x1+3: case down1+3: case y1+3: case z1+3:
1936
case fnt1+3: case xxx1+3: case fnt_def1+3:
1937
return mpx_signed_quad(mpx);
1939
case nop: case bop: case eop: case push: case pop: case pre: case post:
1940
case post_post: case undefined_commands:
1943
case w0: return mpx->w; break;
1944
case x0: return mpx->x; break;
1945
case y0: return mpx->y; break;
1946
case z0: return mpx->z; break;
1947
case sixty_four_cases(fnt_num_0):
1948
return (web_integer)(o-fnt_num_0);
1951
return 0; /* compiler warning */
1954
@ Here is the |do_dvi_commands| procedure.
1957
static void mpx_do_dvi_commands (MPX mpx) {
1958
unsigned int o; /* operation code of the current command */
1959
web_integer p,q; /* parameters of the current command */
1960
web_integer cur_font; /* current internal font number */
1961
if ( (mpx->cur_fbase<mpx->cur_ftop) && (mpx->buf_ptr<virtual_space) )
1962
cur_font=mpx_select_font(mpx, mpx->font_num[mpx->cur_ftop-1]); /* select first local font */
1964
cur_font=max_fnums+1; /* current font is undefined */
1965
mpx->w=0; mpx->x=0; mpx->y=0; mpx->z=0; /* initialize the state variables */
1967
@<Translate the next command in the \.{DVI} file; |return| if it was |eop|@>;
1971
@ The multiway switch in |first_par|, above, was organized by the length
1972
of each command; the one in |do_dvi_commands| is organized by the semantics.
1974
@ @<Translate the next command...@>=
1976
o=(unsigned int)mpx_get_byte(mpx);
1977
p=mpx_first_par(mpx, o);
1978
if ( feof(mpx->dvi_file) )
1979
bad_dvi("the DVI file ended prematurely");
1980
@.the DVI file ended prematurely@>
1981
if ( o<set1+4 ) { /* |set_char_0| through |set_char_127|, |set1| through |set4| */
1982
if ( cur_font>max_fnums ) {
1983
if ( mpx->vf_reading )
1984
mpx_abort(mpx,"no font selected for character %d in virtual font", p);
1986
bad_dvi_two("no font selected for character %d",p);
1988
@.no font selected@>
1989
mpx_set_virtual_char(mpx, cur_font,p);
1990
mpx->h += @<Width of character |p| in font |cur_font|@>;
1993
case four_cases(put1):
1994
mpx_set_virtual_char(mpx, cur_font, p);
1997
q=(web_integer)trunc(mpx_signed_quad(mpx)*mpx->dvi_scale);
1998
mpx_do_set_rule(mpx, (web_integer)trunc(p*mpx->dvi_scale),q);
2002
q=(web_integer)trunc(mpx_signed_quad(mpx)*mpx->dvi_scale);
2003
mpx_do_set_rule(mpx, (web_integer)trunc(p*mpx->dvi_scale),q);
2005
@<Additional cases for translating \.{DVI} command |o| with
2006
first parameter |p|@>@;
2007
case undefined_commands:
2008
bad_dvi_two("undefined command %d",o);
2009
@.undefined command@>
2011
} /* all cases have been enumerated */
2015
@ @<Additional cases for translating \.{DVI} command |o|...@>=
2016
case four_cases(xxx1):
2019
case pre: case post: case post_post:
2020
bad_dvi("preamble or postamble within a page!");
2021
@.preamble or postamble within a page@>
2024
@ @<Additional cases for translating \.{DVI} command |o|...@>=
2028
bad_dvi("bop occurred before eop");
2029
@.bop occurred before eop@>
2041
@ @<Additional cases for translating \.{DVI} command |o|...@>=
2042
case four_cases(right1):
2043
mpx->h += trunc(p*mpx->dvi_scale);
2045
case w0: case four_cases(w1):
2046
mpx->w = (web_integer)trunc(p*mpx->dvi_scale); mpx->h += mpx->w;
2048
case x0: case four_cases(x1):
2049
mpx->x = (web_integer)trunc(p*mpx->dvi_scale); mpx->h += mpx->x;
2051
case four_cases(down1):
2052
mpx->v += trunc(p*mpx->dvi_scale);
2054
case y0: case four_cases(y1):
2055
mpx->y = (web_integer)trunc(p*mpx->dvi_scale); mpx->v += mpx->y;
2057
case z0: case four_cases(z1):
2058
mpx->z = (web_integer)trunc(p*mpx->dvi_scale); mpx->v += mpx->z;
2061
@ @<Additional cases for translating \.{DVI} command |o|...@>=
2062
case sixty_four_cases(fnt_num_0): case four_cases(fnt1):
2063
cur_font = mpx_select_font(mpx, p);
2065
case four_cases(fnt_def1):
2066
mpx_define_font(mpx, p);
2069
@* The main program.
2070
Now we are ready to put it all together. This is where \.{DVItoMP} starts,
2074
static int mpx_dvitomp (MPX mpx, char *dviname) {
2076
mpx->dviname = dviname;
2077
mpx_open_dvi_file(mpx);
2078
@<Process the preamble@>;
2079
mpx_open_mpxfile(mpx);
2080
if (mpx->banner!=NULL)
2081
fprintf (mpx->mpxfile,"%s\n",mpx->banner);
2083
@<Advance to the next |bop| command@>;
2085
(void)mpx_signed_quad(mpx);
2086
@<Do initialization required before starting a new page@>;
2087
mpx_start_picture(mpx);
2088
mpx_do_dvi_commands(mpx);
2089
if ( mpx->stk_siz!=0 )
2090
bad_dvi("stack not empty at end of page");
2091
@.stack not empty...@>
2092
mpx_stop_picture(mpx);
2093
fprintf(mpx->mpxfile,"mpxbreak\n");
2096
mpx_fclose(mpx,mpx->dvi_file);
2097
if ( mpx->history<=mpx_cksum_trouble )
2100
return mpx->history;
2103
@ The main program needs a few global variables in order to do its work.
2106
web_integer k;web_integer p; /* general purpose registers */
2107
web_integer numerator;web_integer denominator; /* stated conversion ratio */
2109
@ @<Process the preamble@>=
2112
p=mpx_get_byte(mpx); /* fetch the first byte */
2114
bad_dvi("First byte isn""t start of preamble!");
2115
@.First byte isn't...@>
2116
p=mpx_get_byte(mpx); /* fetch the identification byte */
2118
mpx_warn(mpx,"identification in byte 1 should be %d!", id_byte);
2119
@.identification...should be n@>
2120
@<Compute the conversion factor@>;
2121
p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */
2124
(void)mpx_get_byte(mpx);
2128
@ The conversion factor |conv| is figured as follows: There are exactly
2129
|n/d| decimicrons per \.{DVI} unit, and 254000 decimicrons per inch,
2130
and |resolution| pixels per inch. Then we have to adjust this
2131
by the stated amount of magnification. No such adjustment is needed for
2132
|dvi_per_fix| since it is used to convert design sizes.
2134
@<Compute the conversion factor@>=
2135
mpx->numerator=mpx_signed_quad(mpx); mpx->denominator=mpx_signed_quad(mpx);
2136
if ( (mpx->numerator<=0)||(mpx->denominator<=0) )
2137
bad_dvi("bad scale ratio in preamble");
2139
mpx->mag=mpx_signed_quad(mpx)/1000.0;
2140
if ( mpx->mag<=0.0 )
2141
bad_dvi("magnification isn't positive");
2142
@.magnification isn't positive@>
2143
mpx->conv=(mpx->numerator/254000.0)*(72.0/mpx->denominator)*mpx->mag;
2144
mpx->dvi_per_fix=(254000.0/mpx->numerator)*(mpx->denominator/72.27)/1048576.0;
2146
@ @<Advance to the next |bop| command@>=
2149
k=mpx_get_byte(mpx);
2150
if ( (k>=fnt_def1)&&(k<fnt_def1+4) ){
2151
p=mpx_first_par(mpx, (unsigned int)k);
2152
mpx_define_font(mpx, p); k=nop;
2158
bad_dvi("missing bop");
2168
These changes support \.{dvips}-style ``\.{color push NAME}'' and
2169
``\.{color pop}'' specials. We store a list of named colors, sorted by
2170
name, and decorate the relevant drawing commands with ``\.{withcolor
2171
(r,g,b)}'' specifiers while a color is defined.
2173
@ A constant bounding the size of the named-color array.
2175
@d max_named_colors 100 /* maximum number of distinct named colors */
2177
@ Then we declare a record for color types.
2180
typedef struct named_color_record {
2181
const char *name; /* color name */
2182
const char *value; /* text to pass to MetaPost */
2183
} named_color_record;
2185
@ Declare the named-color array itself.
2188
named_color_record named_colors[(max_named_colors+1)];
2189
/* stores information about named colors, in sorted order by name */
2190
web_integer num_named_colors; /* number of elements of |named_colors| that are valid */
2192
@ This function, used only during initialization, defines a named color.
2195
static void mpx_def_named_color (MPX mpx, const char *n, const char *v) {
2196
mpx->num_named_colors++;
2197
assert(mpx->num_named_colors<max_named_colors);
2198
mpx->named_colors[mpx->num_named_colors].name = n;
2199
mpx->named_colors[mpx->num_named_colors].value = v;
2203
static void mpx_def_named_color (MPX mpx, const char *n, const char *v);
2205
@ During the initialization phase, we define values for all the named
2206
colors defined in \.{colordvi.tex}. CMYK-to-RGB conversion by GhostScript.
2208
This list has to be sorted alphabetically!
2210
@<Set initial values@>=
2211
mpx->num_named_colors = 0;
2212
mpx_def_named_color(mpx, "Apricot", "(1.0, 0.680006, 0.480006)");
2213
mpx_def_named_color(mpx, "Aquamarine", "(0.180006, 1.0, 0.7)");
2214
mpx_def_named_color(mpx, "Bittersweet", "(0.760012, 0.0100122, 0.0)");
2215
mpx_def_named_color(mpx, "Black", "(0.0, 0.0, 0.0)");
2216
mpx_def_named_color(mpx, "Blue", "(0.0, 0.0, 1.0)");
2217
mpx_def_named_color(mpx, "BlueGreen", "(0.15, 1.0, 0.669994)");
2218
mpx_def_named_color(mpx, "BlueViolet", "(0.1, 0.05, 0.960012)");
2219
mpx_def_named_color(mpx, "BrickRed", "(0.719994, 0.0, 0.0)");
2220
mpx_def_named_color(mpx, "Brown", "(0.4, 0.0, 0.0)");
2221
mpx_def_named_color(mpx, "BurntOrange", "(1.0, 0.489988, 0.0)");
2222
mpx_def_named_color(mpx, "CadetBlue", "(0.380006, 0.430006, 0.769994)");
2223
mpx_def_named_color(mpx, "CarnationPink", "(1.0, 0.369994, 1.0)");
2224
mpx_def_named_color(mpx, "Cerulean", "(0.0600122, 0.889988, 1.0)");
2225
mpx_def_named_color(mpx, "CornflowerBlue", "(0.35, 0.869994, 1.0)");
2226
mpx_def_named_color(mpx, "Cyan", "(0.0, 1.0, 1.0)");
2227
mpx_def_named_color(mpx, "Dandelion", "(1.0, 0.710012, 0.160012)");
2228
mpx_def_named_color(mpx, "DarkOrchid", "(0.6, 0.2, 0.8)");
2229
mpx_def_named_color(mpx, "Emerald", "(0.0, 1.0, 0.5)");
2230
mpx_def_named_color(mpx, "ForestGreen", "(0.0, 0.880006, 0.0)");
2231
mpx_def_named_color(mpx, "Fuchsia", "(0.45, 0.00998169, 0.919994)");
2232
mpx_def_named_color(mpx, "Goldenrod", "(1.0, 0.9, 0.160012)");
2233
mpx_def_named_color(mpx, "Gray", "(0.5, 0.5, 0.5)");
2234
mpx_def_named_color(mpx, "Green", "(0.0, 1.0, 0.0)");
2235
mpx_def_named_color(mpx, "GreenYellow", "(0.85, 1.0, 0.310012)");
2236
mpx_def_named_color(mpx, "JungleGreen", "(0.0100122, 1.0, 0.480006)");
2237
mpx_def_named_color(mpx, "Lavender", "(1.0, 0.519994, 1.0)");
2238
mpx_def_named_color(mpx, "LimeGreen", "(0.5, 1.0, 0.0)");
2239
mpx_def_named_color(mpx, "Magenta", "(1.0, 0.0, 1.0)");
2240
mpx_def_named_color(mpx, "Mahogany", "(0.65, 0.0, 0.0)");
2241
mpx_def_named_color(mpx, "Maroon", "(0.680006, 0.0, 0.0)");
2242
mpx_def_named_color(mpx, "Melon", "(1.0, 0.539988, 0.5)");
2243
mpx_def_named_color(mpx, "MidnightBlue", "(0.0, 0.439988, 0.569994)");
2244
mpx_def_named_color(mpx, "Mulberry", "(0.640018, 0.0800061, 0.980006)");
2245
mpx_def_named_color(mpx, "NavyBlue", "(0.0600122, 0.460012, 1.0)");
2246
mpx_def_named_color(mpx, "OliveGreen", "(0.0, 0.6, 0.0)");
2247
mpx_def_named_color(mpx, "Orange", "(1.0, 0.389988, 0.130006)");
2248
mpx_def_named_color(mpx, "OrangeRed", "(1.0, 0.0, 0.5)");
2249
mpx_def_named_color(mpx, "Orchid", "(0.680006, 0.360012, 1.0)");
2250
mpx_def_named_color(mpx, "Peach", "(1.0, 0.5, 0.3)");
2251
mpx_def_named_color(mpx, "Periwinkle", "(0.430006, 0.45, 1.0)");
2252
mpx_def_named_color(mpx, "PineGreen", "(0.0, 0.75, 0.160012)");
2253
mpx_def_named_color(mpx, "Plum", "(0.5, 0.0, 1.0)");
2254
mpx_def_named_color(mpx, "ProcessBlue", "(0.0399878, 1.0, 1.0)");
2255
mpx_def_named_color(mpx, "Purple", "(0.55, 0.139988, 1.0)");
2256
mpx_def_named_color(mpx, "RawSienna", "(0.55, 0.0, 0.0)");
2257
mpx_def_named_color(mpx, "Red", "(1.0, 0.0, 0.0)");
2258
mpx_def_named_color(mpx, "RedOrange", "(1.0, 0.230006, 0.130006)");
2259
mpx_def_named_color(mpx, "RedViolet", "(0.590018, 0.0, 0.660012)");
2260
mpx_def_named_color(mpx, "Rhodamine", "(1.0, 0.180006, 1.0)");
2261
mpx_def_named_color(mpx, "RoyalBlue", "(0.0, 0.5, 1.0)");
2262
mpx_def_named_color(mpx, "RoyalPurple", "(0.25, 0.1, 1.0)");
2263
mpx_def_named_color(mpx, "RubineRed", "(1.0, 0.0, 0.869994)");
2264
mpx_def_named_color(mpx, "Salmon", "(1.0, 0.469994, 0.619994)");
2265
mpx_def_named_color(mpx, "SeaGreen", "(0.310012, 1.0, 0.5)");
2266
mpx_def_named_color(mpx, "Sepia", "(0.3, 0.0, 0.0)");
2267
mpx_def_named_color(mpx, "SkyBlue", "(0.380006, 1.0, 0.880006)");
2268
mpx_def_named_color(mpx, "SpringGreen", "(0.739988, 1.0, 0.239988)");
2269
mpx_def_named_color(mpx, "Tan", "(0.860012, 0.580006, 0.439988)");
2270
mpx_def_named_color(mpx, "TealBlue", "(0.119994, 0.980006, 0.640018)");
2271
mpx_def_named_color(mpx, "Thistle", "(0.880006, 0.410012, 1.0)");
2272
mpx_def_named_color(mpx, "Turquoise", "(0.15, 1.0, 0.8)");
2273
mpx_def_named_color(mpx, "Violet", "(0.210012, 0.119994, 1.0)");
2274
mpx_def_named_color(mpx, "VioletRed", "(1.0, 0.189988, 1.0)");
2275
mpx_def_named_color(mpx, "White", "(1.0, 1.0, 1.0)");
2276
mpx_def_named_color(mpx, "WildStrawberry", "(1.0, 0.0399878, 0.610012)");
2277
mpx_def_named_color(mpx, "Yellow", "(1.0, 1.0, 0.0)");
2278
mpx_def_named_color(mpx, "YellowGreen", "(0.560012, 1.0, 0.260012)");
2279
mpx_def_named_color(mpx, "YellowOrange", "(1.0, 0.580006, 0.0)");
2281
@ Color commands get a separate warning procedure. |warn| sets |history :=
2282
mpx_warning_given|, which causes a nonzero exit status; but color errors are
2283
trivial and should leave the exit status zero.
2285
@d color_warn(A) mpx_warn(mpx,A)
2286
@d color_warn_two(A,B) mpx_warn(mpx,"%s%s",A,B)
2288
@ The |do_xxx| procedure handles DVI specials (defined with the |xxx1...xxx4| commands).
2292
@<Declare procedures to handle color commands@>=
2293
static void mpx_do_xxx (MPX mpx, web_integer p)
2295
unsigned char buf[(XXX_BUF+1)]; /* FIXME: Fixed size buffer. */
2296
web_integer l, r, m, k, len;
2298
int bufsiz = XXX_BUF;
2300
while ( ( p > 0) && (len < bufsiz) ) {
2301
buf[len] = (unsigned char)mpx_get_byte(mpx);
2304
@<Check whether |buf| contains a color command; if not, |goto XXXX|@>
2306
color_warn("long \"color\" special ignored");
2309
if ( @<|buf| contains a color pop command@> ) {
2310
@<Handle a color pop command@>
2311
} else if ( @<|buf| contains a color push command@> ) {
2312
@<Handle a color push command@>
2314
color_warn("unknown \"color\" special ignored");
2318
for (k = 1;k<=p;k++) (void)mpx_get_byte(mpx);
2323
@<Check whether |buf| contains a color command; if not, |goto XXXX|@>=
2333
@ @<|buf| contains a color push command@>=
2341
@ @<|buf| contains a color pop command@>=
2347
@ The \.{color push} and \.{pop} commands imply a color stack, so we need a
2348
global variable to hold that stack.
2350
@d max_color_stack_depth 10 /* maximum depth of saved color stack */
2352
@ Here's the actual stack variables.
2355
web_integer color_stack_depth; /* current depth of saved color stack */
2356
char *color_stack[(max_color_stack_depth+1)]; /* saved color stack */
2358
@ Initialize the stack to empty.
2360
@<Set initial values@>=
2361
mpx->color_stack_depth = 0;
2363
@ \.{color pop} just pops the stack.
2365
@<Handle a color pop command@>=
2366
mpx_finish_last_char(mpx);
2367
if (mpx->color_stack_depth > 0 ) {
2368
free(mpx->color_stack[mpx->color_stack_depth]);
2369
decr(mpx->color_stack_depth);
2371
color_warn("color stack underflow");
2374
@ \.{color push} pushes a color onto the stack.
2376
@<Handle a color push command@>=
2377
mpx_finish_last_char(mpx);
2378
if ( mpx->color_stack_depth >= max_color_stack_depth )
2379
mpx_abort(mpx,"color stack overflow");
2380
incr(mpx->color_stack_depth);
2381
/* I don't know how to do string operations in Pascal. */
2382
/* Skip over extra spaces after 'color push'. */
2384
while ( (l < len - 1) && (buf[l] == ' ') ) incr(l);
2385
if ( @<|buf[l]| contains an rgb command@> ) {
2386
@<Handle a color push rgb command@>
2387
} else if ( @<|buf[l]| contains a cmyk command@> ) {
2388
@<Handle a color push cmyk command@>
2389
} else if ( @<|buf[l]| contains a gray command@> ) {
2390
@<Handle a color push gray command@>
2392
@<Handle a named color push command@>
2395
@ @<|buf[l]| contains an rgb command@>=
2398
&& (buf[l+1] == 'g')
2399
&& (buf[l+2] == 'b')
2400
&& (buf[l+3] == ' ')
2402
@ @<Handle a color push rgb command@>=
2404
while ( (l < len) && (buf[l] == ' ') ) incr(l); /* Remove spaces at end of buf */
2405
while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
2406
mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+3),1);
2408
@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
2410
@ @<|buf[l]| contains a gray command@>=
2413
&& (buf[l+1] == 'r')
2414
&& (buf[l+2] == 'a')
2415
&& (buf[l+3] == 'y')
2416
&& (buf[l+4] == ' ')
2418
@ @<Handle a color push gray command@>=
2420
while ( (l < len) && (buf[l] == ' ') ) incr(l); /* Remove spaces at end of buf */
2421
while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
2422
mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+9),1);
2423
strcpy(mpx->color_stack[mpx->color_stack_depth],"white*");
2425
@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
2427
@ @<|buf[l]| contains a cmyk command@>=
2430
&& (buf[l+1] == 'm')
2431
&& (buf[l+2] == 'y')
2432
&& (buf[l+3] == 'k')
2433
&& (buf[l+4] == ' ')
2435
@ @<Handle a color push cmyk command@>=
2437
while ( (l < len) && (buf[l] == ' ') ) incr(l);
2438
/* Remove spaces at end of buf */
2439
while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
2440
mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+7),1);
2441
strcpy(mpx->color_stack[mpx->color_stack_depth],"cmyk");
2443
@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
2445
@ @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>=
2446
mpx->color_stack[mpx->color_stack_depth][k] = '(';
2449
if ( buf[l] == ' ' ) {
2450
mpx->color_stack[mpx->color_stack_depth][k] = ',';
2451
while ( (l < len) && (buf[l] == ' ') ) incr(l);
2454
mpx->color_stack[mpx->color_stack_depth][k] = (char)buf[l];
2459
mpx->color_stack[mpx->color_stack_depth][k] = ')';
2460
mpx->color_stack[mpx->color_stack_depth][k+1] = 0;
2462
@ Binary-search the |named_colors| array, then push the found color onto
2465
@<Handle a named color push command@>=
2466
for (k = l;k<=len - 1;k++) {
2467
buf[k - l] = xchr(buf[k]);
2470
/* clang: never read: len = len - l; */
2471
l = 1; r = mpx->num_named_colors;
2473
while ( (l <= r) && ! found ) {
2474
m = (l + r) / 2; k = strcmp((char *)(buf), mpx->named_colors[m].name);
2476
mpx->color_stack[mpx->color_stack_depth]=xstrdup(mpx->named_colors[m].value);
2478
} else if ( k < 0 ) {
2485
color_warn_two("non-hardcoded color \"%s\" in \"color push\" command", buf);
2486
mpx->color_stack[mpx->color_stack_depth]=xstrdup((char *)(buf));
2489
@ Last but not least, this code snippet prints a \.{withcolor} specifier
2490
for the top of the color stack, if the stack is nonempty.
2492
@<Print a \.{withcolor} specifier if appropriate@>=
2493
if ( mpx->color_stack_depth > 0 ) {
2494
fprintf(mpx->mpxfile," withcolor %s\n",mpx->color_stack[mpx->color_stack_depth]);
2500
This program reads device-independent troff output files,
2501
and converts them into a symbolic form understood by MetaPost. Some
2502
of the code was borrowed from DVItoMP. It understands all the D? graphics
2503
functions that dpost does but it ignores `x X' device control functions
2504
such as `x X SetColor:...', `x X BeginPath:', and `x X DrawPath:...'.
2506
The output file is a sequence of MetaPost picture expressions, one for every
2507
page in the input file. It makes no difference where the input file comes
2508
from, but it is intended to process the result of running eqn and troff on
2509
the output of MPtoTR. Such a file contains one page for every btex...etex
2510
block in the original input. This program then creates a corresponding
2511
sequence of MetaPost picture expressions for use as an auxiliary input file.
2512
Since MetaPost expects such files to have the extension .mpx, the output
2513
is sometimes called an `mpx' file.
2515
@d SHIFTS 100 /* maximum number of characters with special shifts */
2516
@d MAXCHARS 256 /* character codes fall in the range 0..MAXCHARS-1 */
2518
@d is_specchar(c) (!mpx->gflag && (c)<=2) /* does charcode c identify a special char? */
2519
@d LWscale 0.03 /* line width for graphics as a fraction of pointsize */
2520
@d YCORR 12.0 /* V coordinate of reference point in (big) points */
2523
int next_specfnt[(max_fnums+1)]; /* used to link special fonts together */
2524
int shiftchar[SHIFTS]; /* charcode of character to shift, else -1 */
2525
float shifth[SHIFTS];
2526
float shiftv[SHIFTS]; /* shift vals/fontsize (y is upward) */
2527
int shiftptr; /* number of entries in shift tables */
2528
int shiftbase[(max_fnums+1)]; /* initial index into shifth,shiftv,shiftchar */
2529
int specfnt; /* int. num. of first special font (or FCOUNT) */
2530
int *specf_tail ; /* tail of specfnt list |(*specf_tail==FCOUNT)| */
2531
float cursize; /* current type size in (big) points */
2532
unsigned int curfont; /* internal number for current font */
2533
float Xslant; /* degrees additional slant for all fonts */
2534
float Xheight; /* yscale fonts to this height if nonzero */
2535
float sizescale; /* groff font size scaling factor */
2536
int gflag; /* non-zero if using groff fonts */
2537
float unit; /* (big) points per troff unit (0 when unset) */
2539
@ @<Set initial...@>=
2541
mpx->specfnt = (max_fnums+1);
2542
mpx->specf_tail = &(mpx->specfnt);
2544
mpx->lnno = 0; /* this is a reset */
2546
mpx->h = 0; mpx->v = 0;
2548
@ @<Makempx header information@>=
2549
typedef char *(*mpx_file_finder)(MPX, const char *, const char *, int);
2551
mpx_tfm_format, /* |kpse_tfm_format| */
2552
mpx_vf_format, /* |kpse_vf_format| */
2553
mpx_trfontmap_format, /* |kpse_mpsupport_format| */
2554
mpx_trcharadj_format, /* |kpse_mpsupport_format| */
2555
mpx_desc_format, /* |kpse_troff_font_format| */
2556
mpx_fontdesc_format, /* |kpse_troff_font_format| */
2557
mpx_specchar_format /* |kpse_mpsupport_format| */
2561
mpx_file_finder find_file;
2564
static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype);
2567
static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
2569
if (mode[0] != 'r' || (! access (nam,R_OK)) || ftype) {
2575
@ @<Set initial...@>=
2576
mpx->find_file = mpx_find_file;
2579
static FILE *mpx_fsearch(MPX mpx, const char *nam, int format);
2582
static FILE *mpx_fsearch(MPX mpx, const char *nam, int format) {
2584
char *fname = (mpx->find_file)(mpx, nam, "r", format);
2586
f = fopen(fname, "rb");
2587
mpx_report(mpx,"%p = fopen(%s,\"rb\")",f, fname);
2592
@ Hash tables (or rather: AVL lists)
2601
static int mpx_comp_name (void *p, const void *pa, const void *pb) {
2603
return strcmp (((const avl_entry *) pa)->name,
2604
((const avl_entry *) pb)->name);
2606
static void *destroy_avl_entry (void *pa) {
2608
p = (avl_entry *) pa;
2613
static void *copy_avl_entry (const void *pa) { /* never used */
2616
p = (const avl_entry *) pa;
2617
q = malloc(sizeof(avl_entry));
2619
q->name = strdup(p->name);
2627
static avl_tree mpx_avl_create (MPX mpx) {
2629
t = avl_create(mpx_comp_name,
2632
malloc, free, NULL);
2634
mpx_abort(mpx, "Memory allocation failure");
2638
@ The only two operations on AVL lists are finding already existing
2639
items, or interning new items. Finding is handled by explicit |avl_find|
2640
calls where needed, but it is wise to have a wrapper around |avl_probe|
2641
to check for memory errors.
2644
static void mpx_avl_probe(MPX mpx, avl_tree tab, avl_entry *p) {
2645
avl_entry *r = (avl_entry *)avl_find(p, tab);
2647
if (avl_ins (p, tab, avl_false)<0)
2648
mpx_abort(mpx,"Memory allocation failure");
2655
The standard functions atoi(), atof(), and sscanf() provide ways of reading
2656
numbers from strings but they give no indication of how much of the string
2657
is consumed. These homemade versions don't parse scientific notation.
2660
char *arg_tail; /* char after the number just gotten; NULL on failure */
2663
static int mpx_get_int(MPX mpx, char *s) {
2664
register int i, d, neg;
2667
for (neg = 0;; s++) {
2670
else if (*s != ' ' && *s != '\t')
2673
if (i = *s - '0', 0 > i || i > 9)
2675
while (d = *++s - '0', 0 <= d && d <= 9)
2678
return neg ? -i : i;
2680
mpx->arg_tail = NULL;
2684
@ GROFF font description files use octal character codes
2685
|groff_font(5)|: The code can be any web_integer. If it starts with
2686
a 0 it will be interpreted as octal; if it starts with 0x
2687
or 0X it will be intepreted as hexadecimal.
2690
static int mpx_get_int_map(MPX mpx, char *s) {
2694
i = (int)strtol(s, &(mpx->arg_tail), 0);
2695
if (s == mpx->arg_tail)
2699
mpx->arg_tail = NULL;
2703
@ Troff output files contain few if any non-web_integers, but this program is
2704
prepared to read floats whenever they seem reasonable; i.e., when the
2705
number is not being used for character positioning. (For non-PostScript
2706
applications h and v are usually in pixels and should be web_integers.)
2709
static float mpx_get_float(MPX mpx, char *s) {
2710
register int d, neg, digits;
2711
register float x, y;
2715
for (neg = 0;; s++) {
2718
else if (*s != ' ' && *s != '\t')
2722
while (d = *s - '0', 0 <= d && d <= 9) {
2723
x = (float)10.0 * x + (float)d;
2729
while (d = *++s - '0', 0 <= d && d <= 9) {
2737
mpx->arg_tail = NULL;
2741
return neg ? -x : x;
2744
@ GROFF font description files have metrics field
2745
of comma-separated web_integers. Traditional troff
2746
have a float in this position. The value is not
2747
used anyway - thus just skip the value,
2748
eat all non-space chars.
2751
static float mpx_get_float_map(MPX mpx, char *s) {
2753
while (isspace((unsigned char)*s))
2755
while (!isspace((unsigned char)*s) && *s)
2763
@ Reading Initialization Files
2765
Read the database file, reserve internal font numbers and set
2766
the |font_name| entries. Each line in the database file contains
2767
|<troff-name>\t,PostScript-name>\t<TeX-name>|
2769
|<troff-name>\t,PostScript-name>|
2770
if the \TeX\ name matches the PostScript name. (|\t| means one or more tabs.)
2776
static void mpx_read_fmap(MPX mpx, const char *dbase) {
2779
char *nam; /* a font name being read */
2782
fin = mpx_fsearch(mpx,dbase, mpx_trfontmap_format);
2784
mpx_abort(mpx,"Cannot find %s", dbase);
2786
mpx->trfonts = mpx_avl_create (mpx);
2787
while ((buf = mpx_getline(mpx,fin)) != NULL) {
2788
if (mpx->nfonts == (max_fnums+1))
2789
mpx_abort(mpx,"Need to increase max_fnums");
2791
while (*buf && *buf != '\t')
2795
tmp = xmalloc(sizeof(avl_entry),1);
2796
tmp->name = xmalloc (1,(size_t)(buf-nam)+1);
2797
strncpy(tmp->name,nam,(unsigned int)(buf-nam));
2798
tmp->name[(buf-nam)] = '\0';
2799
tmp->num = (int)mpx->nfonts++;
2800
assert(avl_ins (tmp, mpx->trfonts, avl_false) > 0);
2803
while (*buf == '\t') buf++;
2804
while (*buf && *buf != '\t') buf++; /* skip over psname */
2805
while (*buf == '\t') buf++;
2810
mpx->font_name[tmp->num] = xstrdup(nam);
2811
mpx->font_num[tmp->num] = -1; /* indicate font is not mounted */
2813
mpx_fclose(mpx,fin);
2817
@ Some characters need their coordinates shifted in order to agree with
2818
troff's view of the world. Logically, this information belongs in the
2819
font description files but it actually resides in a PostScript prolog
2820
that the troff output processor dpost reads. Since that file is in
2821
PostScript and subject to change, we read the same information from
2822
a small auxiliary file that gives shift amounts relative to the font
2826
The PostScript prologue in GNU groff's font directory does not
2827
contain any character shift information, so the following function
2828
becomes redundant. Simply keeping an empty "trchars.adj" file
2829
around will do fine without requiring any changes to this program.
2832
static void mpx_read_char_adj(MPX mpx, const char *adjfile) {
2838
fin = mpx_fsearch(mpx,adjfile, mpx_trcharadj_format);
2840
mpx_abort(mpx,"Cannot find %s", adjfile);
2842
for (i = 0; i < mpx->nfonts; i++)
2843
mpx->shiftbase[i] = 0;
2844
while (fgets(buf, 200, fin) != NULL) {
2845
if (mpx->shiftptr == SHIFTS - 1)
2846
mpx_abort(mpx,"Need to increase SHIFTS");
2847
if (buf[0] != ' ' && buf[0] != '\t') {
2848
for (i = 0; buf[i] != '\0'; i++)
2851
mpx->shiftchar[mpx->shiftptr++] = -1;
2853
p = (avl_entry *)avl_find (&tmp, mpx->trfonts);
2855
mpx_abort(mpx,"%s refers to unknown font %s", adjfile, buf);
2856
/* clang: dereference null pointer 'p' */ assert(p);
2857
mpx->shiftbase[p->num] = mpx->shiftptr;
2860
mpx->shiftchar[mpx->shiftptr] = mpx_get_int(mpx,buf);
2861
mpx->shifth[mpx->shiftptr] = mpx_get_float(mpx,mpx->arg_tail);
2862
mpx->shiftv[mpx->shiftptr] = -mpx_get_float(mpx,mpx->arg_tail);
2863
if (mpx->arg_tail == NULL)
2864
mpx_abort(mpx,"Bad shift entry : \"%s\"", buf);
2868
mpx->shiftchar[mpx->shiftptr++] = -1;
2869
mpx_fclose(mpx,fin);
2872
@ Read the DESC file of the troff device to gather information
2873
about sizescale and whether running under groff.
2875
Ignore all commands not specially handled. This relieves
2876
of collecting commands without arguments here and also
2877
makes the program more robust in case of future DESC
2881
static void mpx_read_desc(MPX mpx) {
2882
const char *const k1[] = {
2883
"res", "hor", "vert", "unitwidth", "paperwidth",
2884
"paperlength", "biggestfont", "spare2", "encoding",
2887
const char *const g1[] = {
2888
"family", "paperheight", "postpro", "prepro",
2889
"print", "image_generator", "broken",
2896
fp = mpx_fsearch(mpx,"DESC", mpx_desc_format);
2898
mpx_abort(mpx,"Cannot find DESC");
2899
while (fscanf(fp, "%199s", cmd) != EOF) {
2901
while ((i = getc(fp)) != EOF && i != '\n');
2904
if (strcmp(cmd, "fonts") == 0) {
2905
if (fscanf(fp, "%d", &n) != 1)
2907
for (i = 0; i < n; i++)
2908
if (fscanf(fp, "%*s") == EOF)
2910
} else if (strcmp(cmd, "sizes") == 0) {
2911
while (fscanf(fp, "%d", &n) == 1 && n != 0);
2912
} else if (strcmp(cmd, "styles") == 0 ||
2913
strcmp(cmd, "papersize") == 0) {
2915
while ((i = getc(fp)) != EOF && i != '\n');
2916
} else if (strcmp(cmd, "sizescale") == 0) {
2917
if (fscanf(fp, "%d", &n) == 1)
2918
mpx->sizescale = (float)n;
2920
} else if (strcmp(cmd, "charset") == 0) {
2923
for (i = 0; k1[i]; i++)
2924
if (strcmp(cmd, k1[i]) == 0) {
2925
if (fscanf(fp, "%*s") == EOF)
2930
for (i = 0; g1[i]; i++)
2931
if (strcmp(cmd, g1[i]) == 0) {
2932
if (fscanf(fp, "%*s") == EOF)
2942
@ Given one line from the character description file for the font with
2943
internal number f, save the appropriate data in the charcodes[f] table.
2944
A return value of zero indicates a syntax error.
2947
GNU groff uses an extended font description file format documented
2948
in |groff_font(5)|. In order to allow parsing of groff's font files,
2949
this function needs to be rewritten as follows:
2951
\item{1.}The `metrics' field parsed by |mpx_get_float(lin);| may include
2952
a comma-separated list of up to six decimal web_integers rather
2953
than just a single floating-point number.
2955
\item{2.}The `charcode' field parsed by |lastcode = mpx_get_int(arg_tail);|
2956
may be given either in decimal, octal, or hexadecimal format.
2959
avl_tree charcodes[(max_fnums+1)]; /* hash tables for translating char names */
2962
static int mpx_scan_desc_line(MPX mpx, int f, char *lin) {
2963
static int lastcode;
2967
while (*lin != ' ' && *lin != '\t' && *lin != '\0')
2971
s = xmalloc((size_t)(lin-t+1),1);
2972
strncpy(s,t,(size_t)(lin-t));
2973
*(s+(lin-t)) = '\0';
2974
while (*lin == ' ' || *lin == '\t')
2977
if (lastcode < MAXCHARS) {
2978
tmp = xmalloc(sizeof(avl_entry),1);
2980
tmp->num = lastcode;
2981
mpx_avl_probe (mpx, mpx->charcodes[f],tmp);
2984
(void) mpx_get_float_map(mpx,lin);
2985
(void) mpx_get_int(mpx,mpx->arg_tail);
2986
lastcode = mpx_get_int_map(mpx,mpx->arg_tail);
2987
if (mpx->arg_tail == NULL)
2989
if (lastcode < MAXCHARS) {
2990
tmp = xmalloc(sizeof(avl_entry),1);
2992
tmp->num = lastcode;
2993
mpx_avl_probe (mpx, mpx->charcodes[f],tmp);
2999
@ Read the font description file for the font with the given troff name
3000
and update the data structures. The result is the internal font number.
3003
static int mpx_read_fontdesc(MPX mpx, char *nam) { /* troff name */
3006
FILE *fin; /* input file */
3007
int f; /* internal font number */
3009
if (mpx->unit == 0.0)
3010
mpx_abort(mpx, "Resolution is not set soon enough");
3012
p = (avl_entry *)avl_find (&tmp,mpx->trfonts);
3014
mpx_abort(mpx, "Font was not in map file");
3015
/* clang: dereference null pointer 'p' */ assert(p);
3017
fin = mpx_fsearch(mpx, nam, mpx_fontdesc_format);
3019
mpx_abort(mpx,"Cannot find %s", nam);
3021
if (fgets(buf, 200, fin) == NULL)
3022
mpx_abort(mpx, "Description file for %s ends unexpectedly", nam);
3023
if (strncmp(buf, "special", 7) == 0) {
3024
*(mpx->specf_tail) = f;
3025
mpx->next_specfnt[f] = (max_fnums+1);
3026
mpx->specf_tail = &(mpx->next_specfnt[f]);
3027
} else if (strncmp(buf, "charset", 7) == 0)
3030
mpx->charcodes[f] = mpx_avl_create (mpx);
3031
while (fgets(buf, 200, fin) != NULL)
3032
if (mpx_scan_desc_line(mpx, f, buf) == 0)
3033
mpx_abort(mpx, "%s has a bad line in its description file: %s", nam, buf);
3034
mpx_fclose(mpx,fin);
3038
@ Page and Character Output
3041
boolean graphics_used; /* nonzero if any graphics seen on this page */
3043
float dmp_str_v; /* corrected start pos for current out string */
3044
float dmp_str_h2; /* where the current output string ends */
3045
float str_size; /* point size for this text string */
3048
@ Print any transformations required by the current Xslant and Xheight settings.
3051
static void mpx_slant_and_ht(MPX mpx);
3054
static void mpx_slant_and_ht(MPX mpx) {
3056
fprintf(mpx->mpxfile, "(");
3057
if (mpx->Xslant != 0.0) {
3058
fprintf(mpx->mpxfile, " slanted%.5f", mpx->Xslant);
3061
if (mpx->Xheight != mpx->cursize && mpx->Xheight != 0.0 && mpx->cursize != 0.0) {
3062
fprintf(mpx->mpxfile, " yscaled%.4f", mpx->Xheight / mpx->cursize);
3065
fprintf(mpx->mpxfile, ")");
3069
@ Output character number c in the font with internal number f.
3072
static void mpx_set_num_char(MPX mpx, int f, int c) {
3073
float hh, vv; /* corrected versions of h, v */
3078
for (i = mpx->shiftbase[f]; mpx->shiftchar[i] >= 0 && i < SHIFTS; i++)
3079
if (mpx->shiftchar[i] == c) {
3080
hh += (mpx->cursize / mpx->unit) * mpx->shifth[i];
3081
vv += (mpx->cursize / mpx->unit) * mpx->shiftv[i];
3084
if (hh - mpx->dmp_str_h2 >= 1.0 || mpx->dmp_str_h2 - hh >= 1.0 ||
3085
vv - mpx->dmp_str_v >= 1.0 || mpx->dmp_str_v - vv >= 1.0 ||
3086
f != mpx->str_f || mpx->cursize != mpx->str_size) {
3087
if (mpx->str_f >= 0)
3088
mpx_finish_last_char(mpx);
3089
else if (!mpx->fonts_used)
3090
mpx_prepare_font_use(mpx); /* first font usage on this page */
3091
if (!mpx->font_used[f])
3092
mpx_first_use(mpx,f); /* first use of font f on this page */
3093
fprintf(mpx->mpxfile, "_s((");
3096
mpx->dmp_str_v = vv;
3097
mpx->dmp_str_h1 = hh;
3098
mpx->str_size = mpx->cursize;
3100
mpx_print_char(mpx, (unsigned char)c);
3101
mpx->dmp_str_h2 = hh + (float)char_width(f,c);
3107
static void mpx_set_string(MPX mpx, char *cname) {
3108
float hh; /* corrected version of h, current horisontal position */
3113
mpx_set_num_char(mpx,(int)mpx->curfont, *cname);
3114
hh += (float)char_width(mpx->curfont,(int)*cname);
3116
mpx_print_char(mpx,(unsigned char)*cname);
3117
hh += (float)char_width(mpx->curfont,(int)*cname);
3119
mpx->h = (web_integer)floor(hh+0.5);
3120
mpx_finish_last_char(mpx);
3123
@ Special Characters
3125
Given the troff name of a special character, this routine finds its
3126
definition and copies it to the MPX file. It also finds the name of
3127
the vardef macro and returns that name. The name should be C.<something>.
3130
TH: A bit of trickery is added here for case-insensitive
3131
file systems. This aliasing allows the CHARLIB directory
3132
to exist on DVDs, for example.
3133
It is a hack, I know. I've stuck to names on TeXLive.
3135
@d test_redo_search do {
3137
deff = mpx_fsearch(mpx, cname, mpx_specchar_format);
3141
static char *mpx_copy_spec_char(MPX mpx, char *cname) {
3145
char specintro[] = "vardef "; /* MetaPost name follows this */
3146
unsigned k = 0; /* how much of specintro so far */
3147
if (strcmp(cname, "ao") == 0) {
3148
deff = mpx_fsearch(mpx, "ao.x", mpx_specchar_format);
3150
} else if (strcmp(cname, "lh") == 0) {
3151
deff = mpx_fsearch(mpx, "lh.x", mpx_specchar_format);
3153
} else if (strcmp(cname, "~=") == 0) {
3154
deff = mpx_fsearch(mpx, "twiddle", mpx_specchar_format);
3157
deff = mpx_fsearch(mpx, cname, mpx_specchar_format);
3160
mpx_abort(mpx, "No vardef in charlib/%s", cname);
3162
while (k < (unsigned)strlen(specintro)) {
3163
if ((c = getc(deff)) == EOF)
3164
mpx_abort(mpx, "No vardef in charlib/%s", cname);
3165
putc(c, mpx->mpxfile);
3166
if (c == specintro[k])
3171
s = xmalloc(mpx->bufsize,1);
3173
while ((c = getc(deff)) != '(') {
3175
mpx_abort(mpx, "vardef in charlib/%s has no arguments", cname);
3176
putc(c, mpx->mpxfile);
3179
putc(c, mpx->mpxfile);
3181
while ((c = getc(deff)) != EOF);
3182
putc(c, mpx->mpxfile);
3187
@ When given a character name instead of a number, we need to check if
3188
it is a special character and download the definition if necessary.
3189
If the character is not in the current font we have to search the special
3195
@ The |spec_tab| avl table combines character names with macro names.
3204
static void mpx_set_char(MPX mpx, char *cname) {
3209
if (*cname == ' ' || *cname == '\t')
3211
f = (int)mpx->curfont;
3213
p = avl_find(&tmp, mpx->charcodes[f]);
3215
for (f = mpx->specfnt; f != (max_fnums+1); f = mpx->next_specfnt[f]) {
3216
p = avl_find(&tmp, mpx->charcodes[f]);
3220
mpx_abort(mpx, "There is no character %s", cname);
3223
/* clang: dereference null pointer 'p' */ assert(p);
3225
if (!is_specchar(c)) {
3226
mpx_set_num_char(mpx, f, c);
3228
if (mpx->str_f >= 0)
3229
mpx_finish_last_char(mpx);
3230
if (!mpx->fonts_used)
3231
mpx_prepare_font_use(mpx);
3232
if (!mpx->font_used[f])
3233
mpx_first_use(mpx, f);
3235
mpx->spec_tab = mpx_avl_create (mpx);
3236
sp = xmalloc(sizeof(spec_entry),1);
3240
spec_entry *r = (spec_entry *)avl_find(sp, mpx->spec_tab);
3242
if (avl_ins (sp, mpx->spec_tab, avl_false)<0)
3243
mpx_abort(mpx,"Memory allocation failure");
3246
if (sp->mac == NULL) {
3247
sp->mac = mpx_copy_spec_char(mpx, cname); /* this won't be NULL */
3249
fprintf(mpx->mpxfile, "_s(%s(_n%d)", sp->mac,f);
3250
fprintf(mpx->mpxfile, ",%.5f,%.4f,%.4f)",
3251
(mpx->cursize/mpx->font_design_size[f])*1.00375,
3252
(double)(((float)mpx->h*mpx->unit)/100.0), YCORR-(float)mpx->v*mpx->unit);
3253
mpx_slant_and_ht(mpx);
3254
fprintf(mpx->mpxfile, ";\n");
3260
Mount the font with troff name nam at external font number n and read any
3261
necessary font files.
3264
static void mpx_do_font_def(MPX mpx, int n, char *nam) {
3269
p = (avl_entry *) avl_find (&tmp, mpx->trfonts);
3271
mpx_abort(mpx, "Font %s was not in map file", nam);
3272
/* clang: dereference null pointer 'p' */ assert(p);
3274
if ( mpx->charcodes[f] == NULL) {
3275
mpx_read_fontdesc(mpx, nam);
3276
mpx->cur_name = xstrdup(mpx->font_name[f]);
3277
if (! mpx_open_tfm_file(mpx) )
3278
font_abort("No TFM file found for ",f);
3279
@.no TFM file found@>
3282
for (k = 0; k < mpx->nfonts; k++)
3283
if (mpx->font_num[k] == n)
3284
mpx->font_num[k] = -1;
3285
mpx->font_num[f] = n;
3286
@<Do any other initialization required for the new font |f|@>;
3291
@ Time on `makepath pencircle'
3293
Given the control points of a cubic Bernstein polynomial, evaluate it at t.
3295
@d Speed ((float) (PI/4.0))
3298
static float mpx_b_eval(const float *xx, float t) {
3301
for (i = 0; i <= 3; i++)
3303
for (i = 3; i > 0; i--)
3304
for (j = 0; j < i; j++)
3305
zz[j] += t * (zz[j + 1] - zz[j]);
3310
@ Find the direction angle at time t on the path `makepath pencircle'.
3311
The tables below give the Bezier control points for MetaPost's cubic
3312
approximation to the first octant of a unit circle.
3315
static const float xx[] = { 1.0, 1.0, (float)0.8946431597, (float)0.7071067812 };
3316
static const float yy[] = { 0.0, (float)0.2652164899, (float)0.5195704026, (float)0.7071067812 };
3319
static float mpx_circangle(float t) {
3321
ti = (float)floor(t);
3323
return (float) atan(mpx_b_eval(yy, t) /
3324
mpx_b_eval(xx, t)) + ti * Speed;
3328
@ Find the spline parameter where `makepath pencircle' comes closest to
3329
(cos(a)/2,sin(a)/2).
3332
static float mpx_circtime(float a) {
3336
for (i = 2; --i >= 0;)
3337
t += (a - mpx_circangle(t)) / Speed;
3347
float gy; /* current point for graphics (init. (h,YCORR/mpx->unit-v) */
3350
static void mpx_prepare_graphics(MPX mpx) {
3352
fprintf(mpx->mpxfile, "vardef _D(expr _d)expr _q =\n");
3353
fprintf(mpx->mpxfile,
3354
" addto _p doublepath _q withpen pencircle scaled _d; enddef;\n");
3355
mpx->graphics_used = true;
3359
@ This function prints the current position (gx,gy). Then if it can read dh dv
3360
from string s, it increments (gx,gy) and prints "--". By returning the rest
3361
of the string s or NULL if nothing could be read from s, it provides the
3362
argument for the next iteration.
3365
static char *mpx_do_line(MPX mpx, char *s) {
3368
fprintf(mpx->mpxfile, "(%.3f,%.3f)", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
3369
dh = mpx_get_float(mpx, s);
3370
dv = mpx_get_float(mpx, mpx->arg_tail);
3371
if (mpx->arg_tail == NULL)
3375
fprintf(mpx->mpxfile, "--\n");
3376
return mpx->arg_tail;
3380
@ Function |spline_seg()| reads two pairs of (dh,dv) increments and prints the
3381
corresponding quadratic B-spline segment, leaving the ending point to be
3382
printed next time. The return value is the string with the first (dh,dv)
3383
pair lopped off. If only one pair of increments is found, we prepare to
3384
terminate the iteration by printing last time's ending point and returning
3388
static char * mpx_spline_seg(MPX mpx, char *s) {
3389
float dh1, dv1, dh2, dv2;
3391
dh1 = mpx_get_float(mpx, s);
3392
dv1 = mpx_get_float(mpx, mpx->arg_tail);
3393
if (mpx->arg_tail == NULL)
3394
mpx_abort(mpx, "Missing spline increments");
3396
fprintf(mpx->mpxfile, "(%.3f,%.3f)", (mpx->gx + .5 * dh1) * mpx->unit,
3397
(mpx->gy - .5 * dv1) * mpx->unit);
3400
dh2 = mpx_get_float(mpx, s);
3401
dv2 = mpx_get_float(mpx, mpx->arg_tail);
3402
if (mpx->arg_tail == NULL)
3404
fprintf(mpx->mpxfile, "..\ncontrols (%.3f,%.3f) and (%.3f,%.3f)..\n",
3405
(mpx->gx - dh1 / 6.0) * mpx->unit, (mpx->gy + dv1 / 6.0) * mpx->unit,
3406
(mpx->gx + dh2 / 6.0) * mpx->unit, (mpx->gy - dv2 / 6.0) * mpx->unit);
3411
@ Draw an ellipse with the given major and minor axes.
3414
static void mpx_do_ellipse(MPX mpx, float a, float b) {
3416
fprintf(mpx->mpxfile, "makepath(pencircle xscaled %.3f\n yscaled %.3f",
3417
a * mpx->unit, b * mpx->unit);
3418
fprintf(mpx->mpxfile, " shifted (%.3f,%.3f));\n", (mpx->gx + .5 * a) * mpx->unit,
3419
mpx->gy * mpx->unit);
3424
@ Draw a counter-clockwise arc centered at (cx,cy) with initial and final radii
3425
(ax,ay) and (bx,by) respectively.
3429
void mpx_do_arc(MPX mpx, float cx, float cy, float ax, float ay, float bx, float by) {
3432
t1 = mpx_circtime((float)atan2(ay, ax));
3433
t2 = mpx_circtime((float)atan2(by, bx));
3436
fprintf(mpx->mpxfile, "subpath (%.5f,%.5f) of\n", t1, t2);
3437
fprintf(mpx->mpxfile,
3438
" makepath(pencircle scaled %.3f shifted (%.3f,%.3f));\n",
3439
2.0 * sqrt(ax * ax + ay * ay) * mpx->unit, cx * mpx->unit, cy * mpx->unit);
3446
@ String s is everything following the initial `D' in a troff graphics command.
3449
static void mpx_do_graphic(MPX mpx, char *s) {
3450
float h1, v1, h2, v2;
3452
mpx_finish_last_char(mpx);
3453
/* GROFF uses Fd to set fill color for solid drawing objects to the
3454
default, so just ignore that.
3456
if (s[0] == 'F' && s[1] == 'd')
3458
mpx->gx = (float) mpx->h;
3459
mpx->gy = (float)YCORR / mpx->unit - ((float) mpx->v);
3460
if (!mpx->graphics_used)
3461
mpx_prepare_graphics(mpx);
3462
fprintf(mpx->mpxfile, "D(%.4f) ", LWscale * mpx->cursize);
3465
h1 = mpx_get_float(mpx,s);
3466
if (mpx->arg_tail == NULL)
3467
mpx_abort(mpx,"Bad argument in %s", s-2);
3468
mpx_do_ellipse(mpx,h1, h1);
3471
h1 = mpx_get_float(mpx,s);
3472
v1 = mpx_get_float(mpx,mpx->arg_tail);
3473
if (mpx->arg_tail == NULL)
3474
mpx_abort(mpx,"Bad argument in %s", s - 2);
3475
mpx_do_ellipse(mpx,h1, v1);
3478
fprintf(mpx->mpxfile, "reverse ");
3481
h1 = mpx_get_float(mpx,s);
3482
v1 = mpx_get_float(mpx,mpx->arg_tail);
3483
h2 = mpx_get_float(mpx,mpx->arg_tail);
3484
v2 = mpx_get_float(mpx,mpx->arg_tail);
3485
if (mpx->arg_tail == NULL)
3486
mpx_abort(mpx,"Bad argument in %s", s - 2);
3487
mpx_do_arc(mpx,mpx->gx + h1, mpx->gy - v1, -h1, v1, h2, -v2);
3492
s = mpx_do_line(mpx,s);
3493
fprintf(mpx->mpxfile, ";\n");
3497
s = mpx_spline_seg(mpx,s);
3499
fprintf(mpx->mpxfile, ";\n");
3502
fprintf(mpx->mpxfile, "(%.3f,%.3f)--", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
3504
s = mpx_spline_seg(mpx,s);
3506
fprintf(mpx->mpxfile, "--(%.3f,%.3f);\n", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
3509
mpx_abort(mpx,"Unknown drawing function %s", s - 2);
3511
mpx->h = (int) floor(mpx->gx + .5);
3512
mpx->v = (int) floor(YCORR / mpx->unit + .5 - mpx->gy);
3517
@ Interpreting Troff Output
3520
static void mpx_change_font(MPX mpx, int f) {
3521
for (mpx->curfont = 0; mpx->curfont < mpx->nfonts; mpx->curfont++)
3522
if (mpx->font_num[mpx->curfont] == f)
3524
mpx_abort(mpx,"Bad font setting");
3528
@ String s0 is everything following the initial `x' in a troff device control
3529
command. A zero result indicates a stop command.
3532
static int mpx_do_x_cmd(MPX mpx, char *s0)
3539
while (*s == ' ' || *s == '\t')
3543
if (mpx->unit != 0.0)
3544
mpx_abort(mpx,"Attempt to reset resolution");
3545
while (*s != ' ' && *s != '\t')
3547
mpx->unit = mpx_get_float(mpx,s);
3548
if (mpx->unit <= 0.0)
3549
mpx_abort(mpx,"Bad resolution: x %s", s0);
3550
mpx->unit = (float)72.0 / mpx->unit;
3553
while (*s != ' ' && *s != '\t')
3555
n = mpx_get_int(mpx,s);
3556
if (mpx->arg_tail == NULL)
3557
mpx_abort(mpx,"Bad font def: x %s", s0);
3559
while (*s == ' ' || *s == '\t')
3561
mpx_do_font_def(mpx,n, s);
3566
while (*s != ' ' && *s != '\t')
3568
mpx->Xheight = mpx_get_float(mpx,s);
3569
/* GROFF troff output is scaled |groff_out(5)|:
3570
The argument to the s command is in scaled
3571
points (units of points/n, where n is the argument
3572
to the sizescale command in the DESC file.) The
3573
argument to the x Height command is also in scaled points.
3574
sizescale for groff devps is 1000
3576
if (mpx->sizescale != 0.0) {
3577
if (mpx->unit != 0.0)
3578
mpx->Xheight *= mpx->unit; /* ??? */
3580
mpx->Xheight /= mpx->sizescale;
3582
if (mpx->Xheight == mpx->cursize)
3586
while (*s != ' ' && *s != '\t')
3588
mpx->Xslant = mpx_get_float(mpx,s) * ((float)PI / (float)180.0);
3589
x = (float)cos(mpx->Xslant);
3590
if (-1e-4 < x && x < 1e-4)
3591
mpx_abort(mpx,"Excessive slant");
3592
mpx->Xslant = (float)sin(mpx->Xslant) / x;
3601
@ This routine reads commands from the troff output file up to and including
3602
the next `p' or `x s' command. It also calls |set_num_char()| and |set_char()|
3603
to generate output when appropriate. A zero result indicates that there
3604
are no more pages to do.
3607
GNU groff uses an extended device-independent output file format
3608
documented in |groff_out(5)|. In order to allow parsing of groff's
3609
output files, this function either needs to be extended to support
3610
the new command codes, or else the use of the "t" and "u" commands
3611
must be disabled by removing the line "tcommand" from the DESC file
3612
in the \$(prefix)/lib/groff/devps directory.
3615
static int mpx_do_page (MPX mpx, FILE *trf) {
3619
mpx->h = mpx->v = 0;
3620
while ((buf = mpx_getline(mpx, trf)) != NULL) {
3623
while (*c != '\0') {
3631
mpx->cursize = mpx_get_float(mpx,c + 1);
3632
/* GROFF troff output is scaled
3633
|groff_out(5)|: The argument to the s command is in scaled
3634
points (units of points/n, where n is the argument
3635
to the sizescale command in the DESC file.) The
3636
argument to the x Height command is also in scaled
3638
sizescale for groff devps is 1000
3640
if (mpx->sizescale != 0.0) {
3641
if (mpx->unit != 0.0)
3642
mpx->cursize *= mpx->unit; /* ??? */
3644
mpx->cursize /= mpx->sizescale;
3648
mpx_change_font(mpx, mpx_get_int(mpx,c + 1));
3652
mpx_abort(mpx, "Bad c command in troff output");
3659
while (*cc != ' ' && *cc != '\t' && *cc != '\0');
3662
mpx_set_num_char(mpx, (int)mpx->curfont, mpx_get_int(mpx,c + 1));
3665
mpx->h = mpx_get_int(mpx, c + 1);
3668
mpx->v = mpx_get_int(mpx, c + 1);
3671
mpx->h += mpx_get_int(mpx, c + 1);
3674
mpx->v += mpx_get_int(mpx, c + 1);
3686
if (c[1] < '0' || c[1] > '9' || c[2] == '\0')
3687
mpx_abort(mpx, "Bad nnc command in troff output");
3688
mpx->h += 10 * (c[0] - '0') + c[1] - '0';
3695
(void) mpx_get_int(mpx, c + 1);
3696
(void) mpx_get_int(mpx, mpx->arg_tail);
3699
mpx_do_graphic(mpx, c + 1);
3702
if (!mpx_do_x_cmd(mpx, c + 1))
3708
/* GROFF uses this command to report filename */
3711
/* GROFF uses this command to control color */
3714
/* GROFF uses this command to output a word with additional
3715
white space between characters, not implemented
3717
mpx_abort(mpx, "Bad command in troff output\n"
3718
"change the DESC file for your GROFF PostScript device, remove tcommand");
3720
/* GROFF uses this command to output a word */
3724
while (*cc != ' ' && *cc != '\t' && *cc != '\0');
3727
mpx_set_string(mpx, ++c);
3732
mpx_abort(mpx, "Bad command in troff output");
3738
mpx_set_char(mpx, ++c);
3745
eoln: /* do nothing */ ;
3753
@d dbname "trfonts.map" /* file for table of troff \& TFM font names */
3754
@d adjname "trchars.adj" /* file for character shift amounts */
3757
static int mpx_dmp(MPX mpx, char *infile) {
3759
FILE *trf = mpx_xfopen(mpx,infile, "r");
3761
mpx_read_fmap(mpx,dbname);
3763
mpx_read_char_adj(mpx,adjname);
3764
mpx_open_mpxfile(mpx);
3765
if (mpx->banner != NULL)
3766
fprintf (mpx->mpxfile,"%s\n",mpx->banner);
3767
if (mpx_do_page(mpx, trf)) {
3769
@<Do initialization required before starting a new page@>;
3770
mpx_start_picture(mpx);
3771
more = mpx_do_page(mpx,trf);
3772
mpx_stop_picture(mpx);
3773
fprintf(mpx->mpxfile, "mpxbreak\n");
3776
mpx_fclose(mpx,trf);
3777
if ( mpx->history<=mpx_cksum_trouble )
3780
return mpx->history;
3787
Make an MPX file from the labels in a MetaPost source file,
3788
using mpto and either dvitomp (TeX) or dmp (troff).
3790
Started from a shell script initially based on John Hobby's original
3791
version, that was then translated to C by Akira Kakuto (Aug 1997,
3792
Aug 2001), and updated and largely rewritten by Taco Hoekwater (Nov 2006).
3795
Differences between the script and this C version:
3797
The script trapped HUP, INT, QUIT and TERM for cleaning up
3798
temporary files. This is a refinement, and not portable.
3800
The script put its own directory in front of the
3801
executable search PATH. This is not portable either, and
3802
it seems a safe bet that normal users do not have 'mpto',
3803
'dvitomp', or 'dmp' commands in their path.
3805
The command-line '-troff' now also accepts an optional argument.
3807
The troff infile for error diagnostics is renamed "mpxerr.i",
3810
The original script deleted mpx*.* in the cleanup process.
3812
That is a bit harder in C, because it requires reading the contents
3813
of the current directory. The current program assumes that
3814
opendir(), readdir() and closedir() are known everywhere where
3815
the function getcwd() exists (except on WIN32, where it uses
3816
|_findfirst| \& co).
3818
If this assumption is false, you can define |NO_GETCWD|, and makempx
3819
will revert to trying to delete only a few known extensions
3821
There is a -debug switch, preventing the removal of tmp files
3823
@d TMPNAME_EXT(a,b) { strcpy(a,tmpname); strcat(a,b); }
3827
#define TEXERR "mpxerr.tex"
3828
#define DVIERR "mpxerr.dvi"
3829
#define TROFF_INERR "mpxerr.i"
3830
#define TROFF_OUTERR "mpxerr.t"
3833
static void mpx_rename (MPX mpx, const char *a, const char *b) {
3834
mpx_report(mpx,"renaming %s to %s",a,b);
3841
const char *progname;
3845
static void mpx_default_erasetmp(MPX mpx) {
3848
if (mpx->mode==mpx_tex_mode) {
3849
wrk = xstrdup(mpx->tex);
3850
p = strrchr(wrk, '.');
3851
*p = '\0'; strcat(wrk, ".aux"); remove(wrk);
3852
*p = '\0'; strcat(wrk, ".pdf"); remove(wrk);
3853
*p = '\0'; strcat(wrk, ".toc"); remove(wrk);
3854
*p = '\0'; strcat(wrk, ".idx"); remove(wrk);
3855
*p = '\0'; strcat(wrk, ".ent"); remove(wrk);
3856
*p = '\0'; strcat(wrk, ".out"); remove(wrk);
3857
*p = '\0'; strcat(wrk, ".nav"); remove(wrk);
3858
*p = '\0'; strcat(wrk, ".snm"); remove(wrk);
3859
*p = '\0'; strcat(wrk, ".tui"); remove(wrk);
3865
static void mpx_erasetmp(MPX mpx);
3868
static void mpx_cleandir(MPX mpx, char *cur_path) {
3871
struct _finddata_t c_file;
3874
struct dirent *entry;
3877
wrk = xstrdup(mpx->tex);
3878
p = strrchr(wrk, '.');
3879
*p = '\0'; /* now wrk is identical to tmpname */
3882
strcat(cur_path,"/*");
3883
if ((hFile = _findfirst (cur_path, &c_file)) == -1L) {
3884
mpx_default_erasetmp(mpx);
3886
if (strstr(c_file.name,wrk)==c_file.name)
3887
remove(c_file.name);
3888
while (_findnext (hFile, &c_file) != -1L) {
3889
if (strstr(c_file.name,wrk)==c_file.name)
3890
remove(c_file.name);
3892
_findclose (hFile); /* no more entries => close directory */
3895
if ((d = opendir(cur_path)) == NULL) {
3896
mpx_default_erasetmp(mpx);
3898
while ((entry = readdir (d)) != NULL) {
3899
if (strstr(entry->d_name,wrk)==entry->d_name)
3900
remove(entry->d_name);
3909
@ It is important that |mpx_erasetmp| remains silent.
3910
If it find trouble, it should just ignore it.
3912
The string |cur_path| is a little bit larger than needed, because that
3913
allows the win32 code in |cleandir| to add the slash and asterisk for
3914
globbing without having to reallocate the variable first.
3918
#define GETCWD _getcwd
3920
#define GETCWD getcwd
3922
static void mpx_erasetmp(MPX mpx) {
3923
char cur_path[1024];
3926
if (mpx->tex[0] != '\0') {
3928
if(GETCWD(cur_path,1020) == NULL) {
3929
mpx_default_erasetmp(mpx); /* don't know where we are */
3931
mpx_cleandir(mpx,cur_path);
3937
@* Running the external typesetters.
3939
First, here is a helper for messaging.
3942
static char *mpx_print_command (MPX mpx, int cmdlength, char **cmdline) {
3948
for (i = 0; i < cmdlength ; i++) {
3949
l += strlen(cmdline[i])+1;
3951
s = xmalloc((size_t)l,1); t=s;
3952
for (i = 0; i < cmdlength ; i++) {
3953
if (i>0) *t++ = ' ';
3954
t = strcpy(t,cmdline[i]);
3955
t += strlen(cmdline[i]);
3960
@ This function unifies the external program calling across Posix-like and Win32
3964
static int do_spawn (MPX mpx, char *icmd, char **options) {
3969
char * cmd = xmalloc(strlen(icmd)+1,1);
3970
if (icmd[0] != '"') {
3973
strncpy(cmd,icmd+1,strlen(icmd)-2);
3974
cmd[strlen(icmd)-2] = 0;
3979
mpx_abort(mpx, "fork failed: %s", strerror(errno));
3981
if(execvp(cmd, options))
3982
mpx_abort(mpx, "exec failed: %s", strerror(errno));
3984
if (wait(&retcode)==child) {
3985
retcode = (WIFEXITED(retcode) ? WEXITSTATUS(retcode) : -1);
3987
mpx_abort(mpx, "wait failed: %s", strerror(errno));
3991
retcode = _spawnvp(_P_WAIT, cmd, (const char* const*)options);
3999
#define nuldev "nul"
4001
#define nuldev "/dev/null"
4003
static int mpx_run_command(MPX mpx, char *inname, char *outname, int count, char **cmdl) {
4006
int sav_o, sav_i; /* for I/O redirection */
4007
FILE *fr, *fw; /* read and write streams for the command */
4009
if (count < 1 || cmdl == NULL || cmdl[0] == NULL)
4010
return -1; /* return non-zero by default, signalling an error */
4012
s = mpx_print_command(mpx,count, cmdl);
4013
mpx_report(mpx,"running command %s", s);
4016
fr = mpx_xfopen(mpx,(inname ? inname : nuldev), "r");
4017
fw = mpx_xfopen(mpx,(outname ? outname : nuldev), "wb");
4018
@<Save and redirect the standard I/O@>;
4019
retcode = do_spawn(mpx,cmdl[0], cmdl);
4020
@<Restore the standard I/O@>;
4026
@ @ Running Troff is more likely than not a series of pipes that
4027
feed input to each other. Makempx does all of this itself by using
4028
temporary files inbetween. That means we have to juggle about with
4029
|stdin| and |stdout|.
4031
This is the only non-ansi C bit of makempx.
4032
@^system dependencies@>
4034
@<Save and redirect the standard I/O@>=
4042
sav_i = DUP(fileno(stdin));
4043
sav_o = DUP(fileno(stdout));
4044
DUPP(fileno(fr), fileno(stdin));
4045
DUPP(fileno(fw), fileno(stdout))
4047
@ @<Restore the standard I/O@>=
4048
DUPP(sav_i, fileno(stdin));
4050
DUPP(sav_o, fileno(stdout));
4053
@ The allocation of the array pointed to by |cmdline_addr| is of
4054
course much larger than is really needed, but it will still only be a
4055
few hunderd bytes at the most, and this ensures that the separate
4056
parts of the |maincmd| will all fit.
4058
@d split_command(a,b) mpx_do_split_command(mpx,a,&b,' ')
4059
@d split_pipes(a,b) mpx_do_split_command(mpx,a,&b,'|')
4063
mpx_do_split_command(MPX mpx, char *maincmd, char ***cmdline_addr, char target) {
4070
if (strlen(maincmd) == 0)
4072
i = sizeof(char *)*(strlen(maincmd)+1);
4073
cmdline = xmalloc(i,1);
4074
memset(cmdline,0,i);
4075
*cmdline_addr = cmdline;
4078
while (maincmd[i] == ' ')
4080
cmd = xstrdup(maincmd);
4082
for (; i <= strlen(maincmd); i++) {
4083
if (in_string == 1) {
4084
if (cmd[i] == '"') {
4087
} else if (in_string == 2) {
4088
if (cmd[i] == '\'') {
4092
if (cmd[i] == '"') {
4094
} else if (cmd[i] == '\'') {
4096
} else if (cmd[i] == target) {
4098
cmdline[ret++] = piece;
4099
while (i < strlen(maincmd) && cmd[(i + 1)] == ' ')
4101
piece = cmd + i + 1;
4106
cmdline[ret++] = piece;
4112
char *maincmd; /* TeX command name */
4115
static void mpx_command_cleanup (MPX mpx, char **cmdline) {
4124
static void mpx_command_error (MPX mpx, int cmdlength, char **cmdline) {
4125
char *s = mpx_print_command(mpx, cmdlength, cmdline);
4126
mpx_command_cleanup(mpx, cmdline);
4127
mpx_abort(mpx, "Command failed: %s; see mpxerr.log", s);
4132
@ @<Makempx header information@>=
4133
typedef struct mpx_options {
4141
mpx_file_finder find_file;
4143
int mpx_makempx (mpx_options *mpxopt) ;
4144
int mpx_run_dvitomp (mpx_options *mpxopt) ;
4149
@d ERRLOG "mpxerr.log"
4150
@d MPXLOG "makempx.log"
4153
int mpx_makempx (mpx_options *mpxopt) {
4155
char **cmdline, **cmdbits;
4158
char tmpname[] = "mpXXXXXX";
4160
int cmdbitlength = 1;
4161
if (!mpxopt->debug) {
4162
@<Check if mp file is newer than mpxfile, exit if not@>;
4164
mpx = malloc(sizeof(struct mpx_data));
4165
if (mpx==NULL || mpxopt->cmd==NULL || mpxopt->mpname==NULL || mpxopt->mpxname==NULL)
4166
return mpx_fatal_error;
4167
mpx_initialize(mpx);
4168
if (mpxopt->banner!=NULL)
4169
mpx->banner = mpxopt->banner;
4170
mpx->mode = mpxopt->mode;
4171
mpx->debug = mpxopt->debug;
4172
if (mpxopt->find_file!=NULL)
4173
mpx->find_file = mpxopt->find_file;
4174
if (mpxopt->cmd!=NULL)
4175
mpx->maincmd = xstrdup(mpxopt->cmd); /* valgrind says this leaks */
4176
mpx->mpname = xstrdup(mpxopt->mpname);
4177
mpx->mpxname = xstrdup(mpxopt->mpxname);
4178
@<Install and test the non-local jump buffer@>;
4181
mpx->errfile = stderr;
4183
mpx->errfile = mpx_xfopen(mpx,MPXLOG, "wb");
4185
mpx->progname = "makempx";
4186
@<Initialize the |tmpname| variable@>;
4187
if (mpxopt->mptexpre == NULL)
4188
mpxopt->mptexpre = xstrdup("mptexpre.tex");
4189
@<Run |mpto| on the mp file@>;
4190
if (mpxopt->cmd==NULL)
4192
if (mpx->mode == mpx_tex_mode) {
4193
@<Run |TeX| and set up |infile| or abort@>;
4194
if (mpx_dvitomp(mpx, infile)) {
4195
mpx_rename(mpx, infile,DVIERR);
4197
remove(mpx->mpxname);
4198
mpx_abort(mpx, "Dvi conversion failed: %s %s\n",
4199
DVIERR, mpx->mpxname);
4201
} else if (mpx->mode == mpx_troff_mode) {
4202
@<Run |Troff| and set up |infile| or abort@>;
4203
if (mpx_dmp(mpx, infile)) {
4204
mpx_rename(mpx,infile, TROFF_OUTERR);
4205
mpx_rename(mpx,mpx->tex, TROFF_INERR);
4207
remove(mpx->mpxname);
4208
mpx_abort(mpx, "Troff conversion failed: %s %s\n",
4209
TROFF_OUTERR, mpx->mpxname);
4212
mpx_fclose(mpx,mpx->mpxfile);
4214
mpx_fclose(mpx,mpx->errfile);
4222
retcode = mpx->history;
4223
mpx_xfree(mpx->buf);
4224
mpx_xfree(mpx->maincmd);
4225
for (i = 0; i < (int)mpx->nfonts; i++)
4226
mpx_xfree(mpx->font_name[i]);
4228
if (retcode == mpx_cksum_trouble)
4232
int mpx_run_dvitomp (mpx_options *mpxopt) {
4235
mpx = malloc(sizeof(struct mpx_data));
4236
if (mpx==NULL || mpxopt->mpname==NULL || mpxopt->mpxname==NULL)
4237
return mpx_fatal_error;
4238
mpx_initialize(mpx);
4239
if (mpxopt->banner!=NULL)
4240
mpx->banner = mpxopt->banner;
4241
mpx->mode = mpxopt->mode;
4242
mpx->debug = mpxopt->debug;
4243
if (mpxopt->find_file!=NULL)
4244
mpx->find_file = mpxopt->find_file;
4245
mpx->mpname = xstrdup(mpxopt->mpname);
4246
mpx->mpxname = xstrdup(mpxopt->mpxname);
4247
@<Install and test the non-local jump buffer@>;
4249
mpx->errfile = stderr;
4251
mpx->errfile = mpx_xfopen(mpx,MPXLOG, "wb");
4253
mpx->progname = "dvitomp";
4254
if (mpx_dvitomp(mpx, mpx->mpname)) {
4256
remove(mpx->mpxname);
4257
mpx_abort(mpx, "Dvi conversion failed: %s %s\n",
4258
DVIERR, mpx->mpxname);
4260
mpx_fclose(mpx,mpx->mpxfile);
4262
mpx_fclose(mpx,mpx->errfile);
4268
retcode = mpx->history;
4269
mpx_xfree(mpx->buf);
4270
for (i = 0; i < (int)mpx->nfonts; i++)
4271
mpx_xfree(mpx->font_name[i]);
4273
if (retcode == mpx_cksum_trouble)
4279
@ \TeX\ has to operate on an actual input file, so we have to append
4280
that to the command line.
4282
@<Run |TeX| and set ...@>=
4285
mpx->maincmd = xrealloc(mpx->maincmd,strlen(mpx->maincmd)+strlen(mpx->tex)+2,1);
4286
strcat(mpx->maincmd, " ");
4287
strcat(mpx->maincmd, mpx->tex);
4288
cmdlength = split_command(mpx->maincmd, cmdline);
4290
retcode = mpx_run_command(mpx, NULL, NULL, cmdlength, cmdline);
4292
TMPNAME_EXT(log, ".log");
4294
TMPNAME_EXT(infile, ".dvi");
4297
mpx_rename(mpx,mpx->tex, TEXERR);
4298
mpx_rename(mpx,log, ERRLOG);
4299
mpx_command_error(mpx, cmdlength, cmdline);
4301
mpx_command_cleanup(mpx, cmdline);
4304
@ @<Run |Troff| and set ...@>=
4306
char *cur_in, *cur_out;
4307
char tmp_a[15], tmp_b[15];
4308
TMPNAME_EXT(tmp_a, ".t");
4309
TMPNAME_EXT(tmp_b, ".tmp");
4313
/* split the command in bits */
4314
cmdbitlength = split_pipes(mpx->maincmd, cmdbits);
4317
for (i = 0; i < cmdbitlength; i++) {
4318
if (cmdline!=NULL) free(cmdline);
4319
cmdlength = split_command(cmdbits[i], cmdline);
4320
retcode = mpx_run_command(mpx, cur_in, cur_out, cmdlength, cmdline);
4323
mpx_rename(mpx,mpx->tex, TROFF_INERR);
4324
mpx_command_error(mpx, cmdlength, cmdline);
4326
if (i < cmdbitlength - 1) {
4336
if (tmp_a!=cur_out) { remove(tmp_a); }
4337
if (tmp_b!=cur_out) { remove(tmp_b); }
4338
strcpy(infile,cur_out);
4341
@ If MPX file is up-to-date or if MP file does not exist, do nothing.
4343
@<Check if mp file is newer than mpxfile, exit if not@>=
4344
if (mpx_newer(mpxopt->mpname, mpxopt->mpxname))
4348
@ The splint comment is here because this use of |sprintf()| is definately safe
4350
@<Initialize the |tmpname| variable@>=
4351
@= /*@@-bufferoverflowhigh@@*/ @>
4353
i = mkstemp(tmpname);
4355
sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
4363
char *tmpstring = mktemp(tmpname);
4364
if ((tmpstring == NULL) || strlen(tmpname) == 0) {
4365
sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
4367
/* this should not really be needed, but better
4369
if (tmpstring != tmpname) {
4370
i = strlen(tmpstring);
4372
strncpy(tmpname, tmpstring, i);
4377
sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
4380
@= /*@@+bufferoverflowhigh@@*/ @>