3 % Copyright 2008 Taco Hoekwater.
\r
5 % This program is free software: you can redistribute it and/or modify
\r
6 % it under the terms of the GNU General Public License as published by
\r
7 % the Free Software Foundation, either version 2 of the License, or
\r
8 % (at your option) any later version.
\r
10 % This program is distributed in the hope that it will be useful,
\r
11 % but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 % GNU General Public License for more details.
\r
15 % You should have received a copy of the GNU General Public License
\r
16 % along with this program. If not, see <http://www.gnu.org/licenses/>.
\r
18 \def\title{Creating mpx files}
\r
19 \def\hang{\hangindent 3em\indent\ignorespaces}
\r
21 \def\LaTeX{{\rm L\kern-.36em\raise.3ex\hbox{\sc a}\kern-.15em
\r
22 T\kern-.1667em\lower.7ex\hbox{E}\kern-.125emX}}
\r
24 \def\(#1){} % this is used to make section names sort themselves better
\r
25 \def\9#1{} % this is used for sort keys in the index
\r
30 @* \[1] Makempx overview.
\r
32 This source file implements the makempx functionality for the new \MP.
\r
33 It includes all of the functional code from the old standalone programs
\r
40 combined into one, with many changes to make all of the code cooperate
\r
45 The local C preprocessor definitions have to come after the C includes
\r
46 in order to prevent name clashes.
\r
56 #include <errno.h> /* TODO autoconf ? */
\r
57 /* unistd.h is needed for every non-Win32 platform, and we assume
\r
58 * that implies that sys/types.h is also present
\r
61 #include <sys/types.h>
\r
67 #include <process.h>
\r
70 # include <sys/wait.h>
\r
73 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
\r
76 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
\r
84 # include <dirent.h>
\r
86 # define dirent direct
\r
87 # if HAVE_SYS_NDIR_H
\r
88 # include <sys/ndir.h>
\r
91 # include <sys/dir.h>
\r
99 #include <sys/stat.h>
\r
104 #define trunc(x) ((integer) (x))
\r
105 #define fabs(x) ((x)<0?(-(x)):(x))
\r
106 #define floor(x) ((integer) (fabs(x)))
\r
108 #define PI 3.14159265358979323846
\r
111 #include "mpxout.h"
\r
116 From the Pascal code of DVItoMP two implicit types are inherited: |boolean| and
\r
119 The more complex datatypes are defined in the following sections.
\r
125 typedef signed int integer;
\r
126 typedef signed int boolean;
\r
130 @ The single most important data structure is the structure
\r
131 |mpx_data|. It contains all of the global state for a specific
\r
132 |makempx| run. A pointer to it is passed as the first argument to just
\r
133 about every function call.
\r
135 One of the fields is a bit special because it is so important: |mode|
\r
136 is the decider between running \TeX\ or Troff as the typesetting
\r
144 typedef struct mpx_data * MPX;
\r
146 @ @<C Data Types@>=
\r
147 @<Types in the outer block@>
\r
148 typedef struct mpx_data {
\r
153 @ Here are some macros for common programming idioms.
\r
155 @d MAXINT 0x7FFFFF /* somewhat arbitrary */
\r
157 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
\r
158 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
\r
160 @ Once an MPX object is allocated, the memory it occupies needs to be
\r
161 initialized to a usable state. This procedure gets things started
\r
164 This function is not allowed to run |mpx_abort| because at this
\r
165 point the jump buffer is not yet initialized, so it should only
\r
166 be used for things that cannot go wrong!
\r
169 static void mpx_initialize (MPX mpx) {
\r
170 memset(mpx,0,sizeof(struct mpx_data));
\r
171 @<Set initial values@>@/
\r
174 @ A global variable |history| keeps track of what type of errors have
\r
175 occurred with the hope that that \MP\ can be warned of any problems.
\r
178 enum mpx_history_states {
\r
179 mpx_spotless=0, /* |history| value when no problems have been found */
\r
180 mpx_cksum_trouble, /* |history| value there have been font checksum mismatches */
\r
181 mpx_warning_given, /* |history| value after a recoverable error */
\r
182 mpx_fatal_error /* |history| value if processing had to be aborted */
\r
190 mpx->history=mpx_spotless;
\r
192 @ The structure has room for the names and the |FILE *| for the
\r
193 input and output files. The most important ones are listed here,
\r
194 the variables for the intermediate files are declared where they
\r
204 int lnno ; /* current line number */
\r
206 @ A set of basic reporting functions.
\r
209 static void mpx_printf(MPX mpx, char *header, char *msg, va_list ap) {
\r
210 fprintf(mpx->errfile, "makempx %s: %s:", header, mpx->mpname);
\r
212 fprintf(mpx->errfile, "%d:", mpx->lnno);
\r
213 fprintf(mpx->errfile, " ");
\r
214 vfprintf(mpx->errfile, msg, ap);
\r
215 fprintf(mpx->errfile, "\n");
\r
219 static void mpx_report(MPX mpx, char *msg, ...) {
\r
221 if (!mpx->debug) return;
\r
223 mpx_printf(mpx, "debug", msg, ap);
\r
225 if ( mpx->history < mpx_warning_given )
\r
226 mpx->history=mpx_cksum_trouble;
\r
230 static void mpx_warn(MPX mpx, char *msg, ...) {
\r
233 mpx_printf(mpx, "warning", msg, ap);
\r
235 if ( mpx->history < mpx_warning_given )
\r
236 mpx->history=mpx_cksum_trouble;
\r
240 static void mpx_error(MPX mpx, char *msg, ...) {
\r
243 mpx_printf(mpx, "error", msg, ap);
\r
245 mpx->history=mpx_warning_given;
\r
248 @ The program uses a |jump_buf| to handle non-local returns,
\r
249 this is initialized at a single spot: the start of |mp_makempx|.
\r
251 @d mpx_jump_out longjmp(mpx->jump_buf,1)
\r
258 static void mpx_abort(MPX mpx, char *msg, ...) {
\r
261 mpx_printf(mpx, "fatal", msg, ap);
\r
263 mpx->history=mpx_fatal_error;
\r
268 @ @<Install and test the non-local jump buffer@>=
\r
269 if (setjmp(mpx->jump_buf) != 0) {
\r
270 int h = mpx->history;
\r
272 xfree(mpx->maincmd);
\r
273 xfree(mpx->mpname);
\r
274 xfree(mpx->mpxname);
\r
280 static FILE *mpx_xfopen (MPX mpx, char *fname, char *fmode) {
\r
281 FILE *f = fopen(fname,fmode);
\r
283 mpx_abort(mpx,"File open error for %s in mode %s", fname, fmode);
\r
286 static void mpx_fclose (MPX mpx, FILE *file) {
\r
288 (void)fclose(file);
\r
292 @d xfree(A) do { mpx_xfree(A); A=NULL; } while (0)
\r
293 @d xrealloc(P,A,B) mpx_xrealloc(mpx,P,A,B)
\r
294 @d xmalloc(A,B) mpx_xmalloc(mpx,A,B)
\r
295 @d xstrdup(A) mpx_xstrdup(mpx,A)
\r
298 static void mpx_xfree (void *x);
\r
299 static void *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) ;
\r
300 static void *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) ;
\r
301 static char *mpx_xstrdup(MPX mpX, const char *s);
\r
304 @ The |max_size_test| guards against overflow, on the assumption that
\r
305 |size_t| is at least 31bits wide.
\r
307 @d max_size_test 0x7FFFFFFF
\r
310 static void mpx_xfree (void *x) {
\r
311 if (x!=NULL) free(x);
\r
313 static void *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) {
\r
315 if ((max_size_test/size)<nmem) {
\r
316 mpx_abort(mpx,"Memory size overflow");
\r
318 w = realloc (p,(nmem*size));
\r
319 if (w==NULL) mpx_abort(mpx,"Out of Memory");
\r
322 static void *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) {
\r
324 if ((max_size_test/size)<nmem) {
\r
325 mpx_abort(mpx,"Memory size overflow");
\r
327 w = malloc (nmem*size);
\r
328 if (w==NULL) mpx_abort(mpx,"Out of Memory");
\r
331 static char *mpx_xstrdup(MPX mpx, const char *s) {
\r
336 if (w==NULL) mpx_abort(mpx,"Out of Memory");
\r
339 @* The command 'newer' became a function.
\r
341 We may have high-res timers in struct stat. If we do, use them.
\r
344 static int mpx_newer(char *source, char *target) {
\r
345 struct stat source_stat, target_stat;
\r
346 #if HAVE_SYS_STAT_H
\r
347 if (stat(target, &target_stat) < 0) return 0; /* true */
\r
348 if (stat(source, &source_stat) < 0) return 1; /* false */
\r
350 if (source_stat.st_mtim.tv_sec > target_stat.st_mtim.tv_sec ||
\r
351 (source_stat.st_mtim.tv_sec == target_stat.st_mtim.tv_sec &&
\r
352 source_stat.st_mtim.tv_nsec >+ target_stat.st_mtim.tv_nsec))
\r
355 if (source_stat.st_mtime >= target_stat.st_mtime)
\r
364 @* Extracting data from \MP\ input.
\r
366 This part of the program transforms a \MP\ input file into a \TeX\ or
\r
367 troff input file by stripping out \.{btex}$\ldots$\.{etex} and
\r
368 \.{verbatimtex}$\ldots$\.{etex} sections.
\r
369 Leading and trailing spaces and tabs are removed from the
\r
370 extracted material and it is surrounded by the preceding and following
\r
371 strings defined immediately below. The input file should be given as
\r
372 argument 1 and the resulting \TeX\ or troff file is written on standard
\r
375 John Hobby wrote the original version, which has since been
\r
376 extensively altered. The current implementation is a bit trickier
\r
377 than I would like, but changing it will take careful study and
\r
378 will likely make it run slower, so I've left it as-is for now.
\r
381 int texcnt ; /* btex..etex blocks so far */
\r
382 int verbcnt ; /* verbatimtex..etex blocks so far */
\r
383 char *bb, *tt, *aa; /* start of before, token, and after strings */
\r
384 char *buf; /* the input line */
\r
387 @ @<Set initial values@>=
\r
388 mpx->bufsize = 1000;
\r
390 @ This function returns NULL on EOF, otherwise it returns |buf|.
\r
393 static char *mpx_getline(MPX mpx, FILE *mpfile) {
\r
398 if (mpx->buf==NULL)
\r
399 mpx->buf = xmalloc(mpx->bufsize,1);
\r
400 while ((c = getc(mpfile)) != EOF && c != '\n' && c != '\r') {
\r
401 mpx->buf[loc++] = c;
\r
402 if (loc == mpx->bufsize) {
\r
403 char *temp = mpx->buf;
\r
404 unsigned n = mpx->bufsize + (mpx->bufsize>>4);
\r
406 mpx_abort(mpx,"Line is too long");
\r
407 mpx->buf = xmalloc(n,1);
\r
408 memcpy(mpx->buf,temp,mpx->bufsize);
\r
424 @ Return nonzero if a prefix of string $s$ matches the null-terminated string $t$
\r
425 and the next character is not a letter or an underscore.
\r
428 static int mpx_match_str(char *s, char *t) {
\r
435 if ((*s>= 'a' && *s<='z') || (*s>= 'A' && *s<='Z') || *s == '_')
\r
441 @ This function tries to express $s$ as the concatenation of three
\r
442 strings $b$, $t$, $a$, with the global pointers $bb$, $tt$, and $aa$ set to the
\r
443 start of the corresponding strings. String $t$ is either a quote mark,
\r
444 a percent sign, or an alphabetic token \.{btex}, \.{etex}, or
\r
445 \.{verbatimtex}. (An alphabetic token is a maximal sequence of letters
\r
446 and underscores.) If there are several possible substrings $t$, we
\r
447 choose the leftmost one. If there is no such $t$, we set $b=s$ and return 0.
\r
450 static int mpx_getbta(MPX mpx, char *s) {
\r
451 int ok = 1; /* zero if last character was |a-z|, |A-Z|, or |_| */
\r
453 for (mpx->tt = mpx->bb; *(mpx->tt) != 0; mpx->tt++) {
\r
454 switch (*(mpx->tt)) {
\r
457 mpx->aa = mpx->tt + 1;
\r
460 if (ok && mpx_match_str(mpx->tt, "btex")) {
\r
461 mpx->aa = mpx->tt + 4;
\r
467 if (ok && mpx_match_str(mpx->tt, "etex")) {
\r
468 mpx->aa = mpx->tt + 4;
\r
474 if (ok && mpx_match_str(mpx->tt, "verbatimtex")) {
\r
475 mpx->aa = mpx->tt + 11;
\r
481 if ((*(mpx->tt) >= 'a' && *(mpx->tt) <= 'z') ||
\r
482 (*(mpx->tt) >= 'A' && *(mpx->tt) <= 'Z') ||
\r
483 (*(mpx->tt) == '_'))
\r
494 static void mpx_copy_mpto (MPX mpx, FILE *outfile) {
\r
495 char *s; /* where a string to print stops */
\r
496 char *t; /* for finding start of last line */
\r
500 if (*mpx->aa == 0) {
\r
501 if ((mpx->aa = mpx_getline(mpx,mpx->mpfile)) == NULL) {
\r
502 mpx_error(mpx,"btex section does not end");
\r
505 if (mpx_getbta(mpx, mpx->aa) && *(mpx->tt) == 'e') {
\r
508 if (*(mpx->tt) == 'b')
\r
509 mpx_error(mpx,"btex in TeX mode");
\r
510 if (*(mpx->tt) == 'v')
\r
511 mpx_error(mpx,"verbatimtex in TeX mode");
\r
517 res = xmalloc(strlen(mpx->bb)+2,1);
\r
518 res = strncpy(res,mpx->bb,(strlen(mpx->bb)+1));
\r
520 res = xrealloc(res,strlen(res)+strlen(mpx->bb)+2,1);
\r
521 res = strncat(res,mpx->bb, strlen(mpx->bb));
\r
524 res = strncat(res, "\n", 1);
\r
526 } while (*(mpx->tt) != 'e');
\r
527 /* whitespace at the end */
\r
528 for (s = res + strlen(res) - 1;
\r
529 s >= res && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'); s--);
\r
532 /* whitespace at the start */
\r
534 s < (res + strlen(res)) && (*s == ' ' || *s == '\t' || *s == '\r'
\r
535 || *s == '\n'); s++);
\r
536 for (; *t != '\n' && t > s; t--);
\r
537 fprintf(outfile,"%s", s);
\r
538 /* put no |%| at end if it's only 1 line total, starting with |%|;
\r
539 * this covers the special case |%&format| in a single line. */
\r
540 if (t != s || *t != '%')
\r
541 fprintf(outfile,"%%");
\r
546 @ Static strings for mpto
\r
549 static const char *mpx_predoc[] = {"", ".po 0\n"};
\r
550 static const char *mpx_postdoc[] = { "\\end{document}\n", ""};
\r
551 static const char *mpx_pretex1[] = {
\r
552 "\\gdef\\mpxshipout{\\shipout\\hbox\\bgroup%\n"
\r
553 " \\setbox0=\\hbox\\bgroup}%\n"
\r
554 "\\gdef\\stopmpxshipout{\\egroup"
\r
555 " \\dimen0=\\ht0 \\advance\\dimen0\\dp0\n"
\r
556 " \\dimen1=\\ht0 \\dimen2=\\dp0\n"
\r
557 " \\setbox0=\\hbox\\bgroup\n"
\r
559 " \\ifnum\\dimen0>0 \\vrule width1sp height\\dimen1 depth\\dimen2 \n"
\r
560 " \\else \\vrule width1sp height1sp depth0sp\\relax\n"
\r
562 " \\ht0=0pt \\dp0=0pt \\box0 \\egroup}\n"
\r
563 "\\mpxshipout%% line %d %s\n", ".lf %d %s\n" };
\r
564 static const char *mpx_pretex[] = { "\\mpxshipout%% line %d %s\n", ".bp\n.lf %d %s\n" };
\r
565 static const char *mpx_posttex[] = { "\n\\stopmpxshipout\n", "\n" };
\r
566 static const char *mpx_preverb1[] = {"", ".lf %d %s\n" }; /* if very first instance */
\r
567 static const char *mpx_preverb[] = { "%% line %d %s\n", ".lf %d %s\n"}; /* all other instances */
\r
568 static const char *mpx_postverb[] = { "\n", "\n" } ;
\r
571 static void mpx_mpto(MPX mpx, char *tmpname, char *mptexpre) {
\r
573 int mode = mpx->mode;
\r
574 char *mpname = mpx->mpname;
\r
575 if (mode==mpx_tex_mode) {
\r
576 TMPNAME_EXT(mpx->tex,".tex");
\r
578 TMPNAME_EXT(mpx->tex,".i");
\r
580 outfile = mpx_xfopen(mpx,mpx->tex, "wb");
\r
581 if (mode==mpx_tex_mode) {
\r
583 if ((fr = fopen(mptexpre, "r"))!= NULL) {
\r
584 while (mpx_getline(mpx, fr) != NULL)
\r
585 fputs(mpx->buf, outfile);
\r
586 mpx_fclose(mpx,fr);
\r
589 mpx->mpfile = mpx_xfopen(mpx,mpname, "r");
\r
590 fprintf(outfile,"%s", mpx_predoc[mode]);
\r
591 while (mpx_getline(mpx, mpx->mpfile) != NULL)
\r
593 fprintf(outfile,"%s", mpx_postdoc[mode]);
\r
594 mpx_fclose(mpx,mpx->mpfile);
\r
595 mpx_fclose(mpx,outfile);
\r
602 mpx->aa = mpx->buf;
\r
603 while (mpx_getbta(mpx, mpx->aa)) {
\r
604 if (*(mpx->tt) == '%') {
\r
606 } else if (*(mpx->tt) == '"') {
\r
608 if (!mpx_getbta(mpx, mpx->aa))
\r
609 mpx_error(mpx,"string does not end");
\r
610 } while (*(mpx->tt) != '"');
\r
611 } else if (*(mpx->tt) == 'b') {
\r
612 if (mpx->texcnt++ == 0)
\r
613 fprintf(outfile,mpx_pretex1[mode], mpx->lnno, mpname);
\r
615 fprintf(outfile,mpx_pretex[mode], mpx->lnno, mpname);
\r
616 mpx_copy_mpto(mpx, outfile);
\r
617 fprintf(outfile,"%s", mpx_posttex[mode]);
\r
618 } else if (*(mpx->tt) == 'v') {
\r
619 if (mpx->verbcnt++ == 0 && mpx->texcnt == 0)
\r
620 fprintf(outfile,mpx_preverb1[mode], mpx->lnno, mpname);
\r
622 fprintf(outfile,mpx_preverb[mode], mpx->lnno, mpname);
\r
623 mpx_copy_mpto(mpx, outfile);
\r
624 fprintf(outfile,"%s", mpx_postverb[mode]);
\r
626 mpx_error(mpx,"unmatched etex");
\r
631 @ @<Run |mpto| on the mp file@>=
\r
632 mpx_mpto(mpx, tmpname, mpxopt->mptexpre)
\r
634 @* DVItoMP Processing.
\r
636 The \.{DVItoMP} program reads binary device-independent (``\.{DVI}'')
\r
637 files that are produced by document compilers such as \TeX, and converts them
\r
638 into a symbolic form understood by \MP. It is loosely based on the \.{DVItype}
\r
639 utility program that produces a more faithful symbolic form of a \.{DVI} file.
\r
641 The output file is a sequence of \MP\ picture expressions, one for every page
\r
642 in the \.{DVI} file. It makes no difference to \.{DVItoMP} where the \.{DVI}
\r
643 file comes from, but it is intended to process the result of running \TeX\
\r
644 or \LaTeX\ on the output of the extraction process that is defined above.
\r
645 Such a \.{DVI} file will contain one page for every \.{btex}$\ldots$\.{etex}
\r
646 block in the original input. Processing with \.{DVItoMP} creates a
\r
647 corresponding sequence of \MP\ picture expressions for use as an auxiliary
\r
648 input file. Since \MP\ expects such files to have the extension \.{.MPX},
\r
649 the output of \.{DVItoMP} is sometimes called an ``\.{MPX}'' file.
\r
651 @ The following parameters can be changed at compile time to extend or
\r
652 reduce \.{DVItoMP}'s capacity.
\r
654 @d virtual_space 1000000 /* maximum total bytes of typesetting commands for virtual fonts */
\r
655 @d max_fonts 1000 /* maximum number of distinct fonts per \.{DVI} file */
\r
656 @d max_fnums 3000 /* maximum number of fonts plus fonts local to virtual fonts */
\r
657 @d max_widths (256*max_fonts) /* maximum number of different characters among all fonts */
\r
658 @d line_length 79 /* maximum output line length (must be at least 60) */
\r
659 @d stack_size 100 /* \.{DVI} files shouldn't |push| beyond this depth */
\r
660 @d font_tolerance 0.00001
\r
661 /* font sizes should match to within this multiple of $2^{20}$ \.{DVI} units */
\r
663 @ If the \.{DVI} file is badly malformed, the whole process must be aborted;
\r
664 \.{DVItoMP} will give up, after issuing an error message about the symptoms
\r
667 @d bad_dvi(A) mpx_abort(mpx,"Bad DVI file: " A "!")
\r
668 @d bad_dvi_two(A,B) mpx_abort(mpx,"Bad DVI file: %s !", A, B)
\r
671 @* The character set.
\r
673 Like all programs written with the \.{WEB} system, \.{DVItoMP} can be
\r
674 used with any character set. It an identify transfrom internally, because
\r
675 the programming for portable input-output is easier when a fixed internal
\r
676 code is used, and because \.{DVI} files use ASCII code for file names.
\r
678 In the conversion from Pascal to C, the |xchr| array has been removed.
\r
679 Because some systems may still want to change the input--output character
\r
680 set, the accesses to |xchr| and |printable| are replaced by macro calls.
\r
682 @d printable(c) (isprint(c) && c < 128 && c!='"')
\r
686 static void mpx_open_mpxfile (MPX mpx) { /* prepares to write text on |mpxfile| */
\r
687 mpx->mpxfile = mpx_xfopen (mpx,mpx->mpxname, "wb");
\r
690 @* Device-independent file format.
\r
691 The format of \.{DVI} files is described in many places including
\r
692 \.{dvitype.web} and Volume~B of D.~E. Knuth's {\sl Computers and Typesetting}.
\r
693 This program refers to the following command codes.
\r
695 @d id_byte 2 /* identifies the kind of \.{DVI} files described here */
\r
697 @d set_char_0 0 /* typeset character 0 and move right */
\r
698 @d set1 128 /* typeset a character and move right */
\r
699 @d set_rule 132 /* typeset a rule and move right */
\r
700 @d put1 133 /* typeset a character */
\r
701 @d put_rule 137 /* typeset a rule */
\r
702 @d nop 138 /* no operation */
\r
703 @d bop 139 /* beginning of page */
\r
704 @d eop 140 /* ending of page */
\r
705 @d push 141 /* save the current positions */
\r
706 @d pop 142 /* restore previous positions */
\r
707 @d right1 143 /* move right */
\r
708 @d w0 147 /* move right by |w| */
\r
709 @d w1 148 /* move right and set |w| */
\r
710 @d x0 152 /* move right by |x| */
\r
711 @d x1 153 /* move right and set |x| */
\r
712 @d down1 157 /* move down */
\r
713 @d y0 161 /* move down by |y| */
\r
714 @d y1 162 /* move down and set |y| */
\r
715 @d z0 166 /* move down by |z| */
\r
716 @d z1 167 /* move down and set |z| */
\r
717 @d fnt_num_0 171 /* set current font to 0 */
\r
718 @d fnt1 235 /* set current font */
\r
719 @d xxx1 239 /* extension to \.{DVI} primitives */
\r
720 @d xxx4 242 /* potentially long extension to \.{DVI} primitives */
\r
721 @d fnt_def1 243 /* define the meaning of a font number */
\r
722 @d pre 247 /* preamble */
\r
723 @d post 248 /* postamble beginning */
\r
724 @d post_post 249 /* postamble ending */
\r
725 @d undefined_commands 250: case 251: case 252: case 253: case 254: case 255
\r
727 @* Input from binary files.
\r
729 @ The program deals with two binary file variables: |dvi_file| is the main
\r
730 input file that we are translating into symbolic form, and |tfm_file| is
\r
731 the current font metric file from which character-width information is
\r
732 being read. It is convenient to have a throw-away variable for function
\r
733 results when reading parts of the files that are being skipped.
\r
736 FILE * dvi_file; /* the input file */
\r
737 FILE * tfm_file; /* a font metric file */
\r
738 FILE * vf_file; /* a virtual font file */
\r
740 @ Prepares to read packed bytes in |dvi_file|
\r
742 static void mpx_open_dvi_file (MPX mpx) {
\r
743 mpx->dvi_file = fopen(mpx->dviname,"rb");
\r
744 if (mpx->dvi_file==NULL)
\r
745 mpx_abort(mpx,"DVI generation failed");
\r
748 @ Prepares to read packed bytes in |tfm_file|
\r
750 static boolean mpx_open_tfm_file (MPX mpx) {
\r
751 mpx->tfm_file = mpx_fsearch(mpx, mpx->cur_name, mpx_tfm_format);
\r
752 if (mpx->tfm_file == NULL)
\r
753 mpx_abort(mpx,"Cannot find TFM %s", mpx->cur_name);
\r
754 free (mpx->cur_name); /* We |xmalloc|'d this before we got called. */
\r
755 return true; /* If we get here, we succeeded. */
\r
758 @ Prepares to read packed bytes in |vf_file|.
\r
759 It's ok if the \.{VF} file doesn't exist.
\r
762 static boolean mpx_open_vf_file (MPX mpx) {
\r
763 mpx->vf_file = mpx_fsearch(mpx, mpx->cur_name, mpx_vf_format);
\r
764 if (mpx->vf_file) {
\r
765 free (mpx->cur_name);
\r
771 @ If you looked carefully at the preceding code, you probably asked,
\r
772 ``What is |cur_name|?'' Good question. It's a global
\r
773 variable: |cur_name| is a string variable that will be set to the
\r
774 current font metric file name before |open_tfm_file| or |open_vf_file|
\r
778 char *cur_name; /* external name */
\r
780 @ It turns out to be convenient to read four bytes at a time, when we are
\r
781 inputting from \.{TFM} files. The input goes into global variables
\r
782 |b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
\r
786 int b0, b1, b2, b3; /* four bytes input at once */
\r
788 @ The |read_tfm_word| procedure sets |b0| through |b3| to the next
\r
789 four bytes in the current \.{TFM} file.
\r
792 static void mpx_read_tfm_word (MPX mpx) {
\r
793 mpx->b0 = getc(mpx->tfm_file);
\r
794 mpx->b1 = getc(mpx->tfm_file);
\r
795 mpx->b2 = getc(mpx->tfm_file);
\r
796 mpx->b3 = getc(mpx->tfm_file);
\r
799 @ Input can come from from three different sources depending on the settings
\r
800 of global variables. When |vf_reading| is true, we read from the \.{VF} file.
\r
801 Otherwise, input can either come directly from |dvi_file| or from a buffer
\r
802 |cmd_buf|. The latter case applies whenever |buf_ptr<virtual_space|.
\r
805 boolean vf_reading; /* should input come from |vf_file|? */
\r
806 unsigned char cmd_buf[(virtual_space+1)]; /* commands for virtual characters */
\r
807 unsigned int buf_ptr; /* |cmd_buf| index for the next byte */
\r
810 mpx->vf_reading=false;
\r
811 mpx->buf_ptr=virtual_space;
\r
813 @ We shall use a set of simple functions to read the next byte or bytes from the
\r
814 current input source. There are seven possibilities, each of which is treated
\r
815 as a separate function in order to minimize the overhead for subroutine calls.
\r
818 static integer mpx_get_byte (MPX mpx) { /* returns the next byte, unsigned */
\r
820 @<Read one byte into |b|@>;
\r
824 static integer mpx_signed_byte (MPX mpx) { /* returns the next byte, signed */
\r
826 @<Read one byte into |b|@>;
\r
827 return ( b<128 ? b : (b-256));
\r
830 static integer mpx_get_two_bytes (MPX mpx) { /* returns the next two bytes, unsigned */
\r
832 a=0; b=0; /* for compiler warnings */
\r
833 @<Read two bytes into |a| and |b|@>;
\r
834 return (a*(int)(256)+b);
\r
837 static integer mpx_signed_pair (MPX mpx) { /* returns the next two bytes, signed */
\r
839 a=0; b=0; /* for compiler warnings */
\r
840 @<Read two bytes into |a| and |b|@>;
\r
841 if ( a<128 ) return (a*256+b);
\r
842 else return ((a-256)*256+b);
\r
845 static integer mpx_get_three_bytes (MPX mpx) { /* returns the next three bytes, unsigned */
\r
846 unsigned char a,b,c;
\r
847 a=0; b=0; c=0; /* for compiler warnings */
\r
848 @<Read three bytes into |a|, |b|, and~|c|@>;
\r
849 return ((a*(int)(256)+b)*256+c);
\r
852 static integer mpx_signed_trio (MPX mpx) { /* returns the next three bytes, signed */
\r
853 unsigned char a,b,c;
\r
854 a=0; b=0; c=0; /* for compiler warnings */
\r
855 @<Read three bytes into |a|, |b|, and~|c|@>;
\r
856 if ( a<128 ) return ((a*(int)(256)+b)*256+c);
\r
857 else return (((a-(int)(256))*256+b)*256+c);
\r
860 static integer mpx_signed_quad (MPX mpx) { /* returns the next four bytes, signed */
\r
861 unsigned char a,b,c,d;
\r
862 a=0; b=0; c=0; d=0; /* for compiler warnings */
\r
863 @<Read four bytes into |a|, |b|, |c|, and~|d|@>;
\r
864 if ( a<128 ) return (((a*(int)(256)+b)*256+c)*256+d);
\r
865 else return ((((a-256)*(int)(256)+b)*256+c)*256+d);
\r
868 @ @<Read one byte into |b|@>=
\r
869 if ( mpx->vf_reading ) {
\r
870 b = getc(mpx->vf_file);
\r
871 } else if ( mpx->buf_ptr==virtual_space ) {
\r
872 b = getc(mpx->dvi_file);
\r
874 b=mpx->cmd_buf[mpx->buf_ptr];
\r
875 incr(mpx->buf_ptr);
\r
878 @ @<Read two bytes into |a| and |b|@>=
\r
879 if ( mpx->vf_reading ) {
\r
880 a = getc(mpx->vf_file);
\r
881 b = getc(mpx->vf_file);
\r
882 } else if ( mpx->buf_ptr==virtual_space ) {
\r
883 a = getc(mpx->dvi_file);
\r
884 b = getc(mpx->dvi_file);
\r
885 } else if ( mpx->buf_ptr+2>mpx->n_cmds ) {
\r
886 mpx_abort(mpx,"Error detected while interpreting a virtual font");
\r
887 @.Error detected while...@>
\r
889 a=mpx->cmd_buf[mpx->buf_ptr];
\r
890 b=mpx->cmd_buf[mpx->buf_ptr+1];
\r
894 @ @<Read three bytes into |a|, |b|, and~|c|@>=
\r
895 if ( mpx->vf_reading ) {
\r
896 a = getc(mpx->vf_file);
\r
897 b = getc(mpx->vf_file);
\r
898 c = getc(mpx->vf_file);
\r
899 } else if ( mpx->buf_ptr==virtual_space ) {
\r
900 a = getc(mpx->dvi_file);
\r
901 b = getc(mpx->dvi_file);
\r
902 c = getc(mpx->dvi_file);
\r
903 } else if ( mpx->buf_ptr+3>mpx->n_cmds ) {
\r
904 mpx_abort(mpx,"Error detected while interpreting a virtual font");
\r
905 @.Error detected while...@>
\r
907 a=mpx->cmd_buf[mpx->buf_ptr];
\r
908 b=mpx->cmd_buf[mpx->buf_ptr+1];
\r
909 c=mpx->cmd_buf[mpx->buf_ptr+2];
\r
913 @ @<Read four bytes into |a|, |b|, |c|, and~|d|@>=
\r
914 if ( mpx->vf_reading ) {
\r
915 a = getc(mpx->vf_file);
\r
916 b = getc(mpx->vf_file);
\r
917 c = getc(mpx->vf_file);
\r
918 d = getc(mpx->vf_file);
\r
919 } else if ( mpx->buf_ptr==virtual_space ) {
\r
920 a = getc(mpx->dvi_file);
\r
921 b = getc(mpx->dvi_file);
\r
922 c = getc(mpx->dvi_file);
\r
923 d = getc(mpx->dvi_file);
\r
924 } else if ( mpx->buf_ptr+4>mpx->n_cmds ) {
\r
925 mpx_abort(mpx,"Error detected while interpreting a virtual font");
\r
926 @.Error detected while...@>
\r
928 a=mpx->cmd_buf[mpx->buf_ptr];
\r
929 b=mpx->cmd_buf[mpx->buf_ptr+1];
\r
930 c=mpx->cmd_buf[mpx->buf_ptr+2];
\r
931 d=mpx->cmd_buf[mpx->buf_ptr+3];
\r
935 @* Data structures for fonts.
\r
937 \.{DVI} file format does not include information about character widths, since
\r
938 that would tend to make the files a lot longer. But a program that reads
\r
939 a \.{DVI} file is supposed to know the widths of the characters that appear
\r
940 in \\{set\_char} commands. Therefore \.{DVItoMP} looks at the font metric
\r
941 (\.{TFM}) files for the fonts that are involved.
\r
942 @.TFM {\rm files}@>
\r
944 @ For purposes of this program, the only thing we need to know about a
\r
945 given character |c| in a non-virtual font |f| is the width. For the font as
\r
946 a whole, all we need is the symbolic name to use in the \.{MPX} file.
\r
948 This information appears implicitly in the following data
\r
949 structures. The current number of fonts defined is |nf|. Each such font has
\r
950 an internal number |f|, where |0<=f<nf|. There is also an external number
\r
951 that identifies the font in the \.{DVI} file. The correspondence is
\r
952 maintained in arrays |font_num| and |internal_num| so that |font_num[i]|
\r
953 is the external number for |f=internal_num[i]|.
\r
954 The external name of this font is the string that occupies |font_name[f]|.
\r
955 The legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive.
\r
956 The \.{TFM} file can specify that some of these are invalid, but this doesn't
\r
957 concern \.{DVItoMP} because it does not do extensive error checking.
\r
958 The width of character~|c| in font~|f| is given by
\r
959 |char_width(f,c)=width[info_base[f]+c]|, and |info_ptr| is the
\r
960 first unused position of the |width| array.
\r
962 If font~|f| is a virtual font, there is a list of \.{DVI} commands for each
\r
963 character. These occupy consecutive positions in the |cmd_buf| array with
\r
964 the commands for character~|c| starting at
\r
965 |start_cmd(f,c)=cmd_ptr[info_base[f]+c]| and ending just before
\r
966 |start_cmd(f,c+1)|. Font numbers used when interpreting these \.{DVI}
\r
967 commands occupy positions |fbase[f]| through |ftop[f]-1| in the |font_num|
\r
968 table and the |internal_num| array gives the corresponding internal font
\r
969 numbers. If such an internal font number~|i| does not correspond to
\r
970 some font occuring in the \.{DVI} file, then |font_num[i]| has not been
\r
971 assigned a meaningful value; this is indicated by |local_only[i]=true|.
\r
973 If font~|f| is not virtual, then |fbase[f]=0| and |ftop[f]=0|. The |start_cmd|
\r
974 values are ignored in this case.
\r
976 @d char_width(A,B) mpx->width[mpx->info_base[(A)]+(B)]
\r
977 @d start_cmd(A,B) mpx->cmd_ptr[mpx->info_base[(A)]+(B)]
\r
980 integer font_num[(max_fnums+1)]; /* external font numbers */
\r
981 integer internal_num[(max_fnums+1)]; /* internal font numbers */
\r
982 boolean local_only[(max_fnums+1)]; /* |font_num| meaningless? */
\r
983 char *font_name[(max_fonts+1)]; /* starting positions of external font names */
\r
984 double font_scaled_size[(max_fonts+1)]; /* scale factors over $2^{20}$ */
\r
985 double font_design_size[(max_fonts+1)]; /* design sizes over $2^{20}$ */
\r
986 integer font_check_sum[(max_fonts+1)]; /* check sum from the |font_def| */
\r
987 integer font_bc[(max_fonts+1)]; /* beginning characters in fonts */
\r
988 integer font_ec[(max_fonts+1)]; /* ending characters in fonts */
\r
989 integer info_base[(max_fonts+1)]; /* index into |width| and |cmd_ptr| tables */
\r
990 integer width[(max_widths+1)];
\r
991 /* character widths, in units $2^{-20}$ of design size */
\r
992 integer fbase[(max_fonts+1)]; /* index into |font_num| for local fonts */
\r
993 integer ftop[(max_fonts+1)]; /* |font_num| index where local fonts stop */
\r
994 integer cmd_ptr[(max_widths+1)]; /* starting positions in |cmd_buf| */
\r
995 unsigned int nfonts; /* the number of known fonts */
\r
996 unsigned int vf_ptr; /* next |font_num| entry for virtual font font tables */
\r
997 unsigned int info_ptr; /* allocation pointer for |width| and |cmd_ptr| tables */
\r
998 unsigned int n_cmds; /* number of occupied cells in |cmd_buf| */
\r
999 unsigned int cur_fbase, cur_ftop;
\r
1000 /* currently applicable part of the |font_num| table */
\r
1002 @ @<Set init...@>=
\r
1003 mpx->nfonts=0; mpx->info_ptr=0; mpx->font_name[0]=0;
\r
1004 mpx->vf_ptr=max_fnums;
\r
1005 mpx->cur_fbase=0; mpx->cur_ftop=0;
\r
1007 @ Printing the name of a given font is easy except that a procedure |print_char|
\r
1008 is needed to actually send an |ASCII_code| to the \.{MPX} file.
\r
1010 @c @<Declare subroutines for printing strings@>@;
\r
1011 static void mpx_print_font (MPX mpx, integer f) { /* |f| is an internal font number */
\r
1012 if ( (f<0)||(f>=(int)mpx->nfonts) ) {
\r
1013 bad_dvi("Undefined font");
\r
1015 char *s = mpx->font_name[f];
\r
1017 mpx_print_char(mpx,*s);
\r
1023 @ Sometimes a font name is needed as part of an error message.
\r
1025 @d font_warn(A,B) mpx_warn (mpx,"%s %s",A,mpx->font_name[(B)])
\r
1026 @d font_error(A,B) mpx_error(mpx,"%s %s",A,mpx->font_name[(B)])
\r
1027 @d font_abort(A,B) mpx_abort(mpx,"%s %s",A,mpx->font_name[(B)])
\r
1030 @ When we encounter a font definition, we save the name, checksum, and size
\r
1031 information, but we don't actually read the \.{TFM} or \.{VF} file until we
\r
1032 are about to use the font. If a matching font is not already defined, we then
\r
1033 allocate a new internal font number.
\r
1035 The following subroutine does the necessary things when a \\{fnt\_def} command
\r
1036 is encountered in the \.{DVI} file or in a \.{VF} file. It assumes that the
\r
1037 first argument has already been parsed and is given by the parameter~|e|.
\r
1039 @c @<Declare a function called |match_font|@>@;
\r
1040 static void mpx_define_font (MPX mpx, integer e) { /* |e| is an external font number */
\r
1041 integer i; /* index into |font_num| and |internal_num| */
\r
1042 integer n; /* length of the font name and area */
\r
1043 integer k; /* general purpose loop counter */
\r
1044 integer x; /* a temporary value for scaled size computation */
\r
1045 if ( mpx->nfonts==max_fonts )
\r
1046 mpx_abort(mpx,"DVItoMP capacity exceeded (max fonts=%d)!", max_fonts);
\r
1047 @.DVItoMP capacity exceeded...@>
\r
1048 @<Allocate an index |i| into the |font_num| and |internal_num| tables@>;
\r
1049 @<Read the font parameters into position for font |nf|@>;
\r
1050 mpx->internal_num[i]=mpx_match_font(mpx, mpx->nfonts,true);
\r
1051 if ( mpx->internal_num[i]==(int)mpx->nfonts ) {
\r
1052 mpx->info_base[mpx->nfonts]=max_widths; /* indicate that the info isn't loaded yet */
\r
1053 mpx->local_only[mpx->nfonts]=mpx->vf_reading; incr(mpx->nfonts);
\r
1057 @ @<Allocate an index |i| into the |font_num| and |internal_num| tables@>=
\r
1058 if ( mpx->vf_ptr==mpx->nfonts )
\r
1059 mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums);
\r
1060 @.DVItoMP capacity exceeded...@>
\r
1061 if ( mpx->vf_reading ) {
\r
1062 mpx->font_num[mpx->nfonts]=0; i=mpx->vf_ptr; decr(mpx->vf_ptr);
\r
1066 mpx->font_num[i]=e
\r
1068 @ @<Read the font parameters into position for font |nf|@>=
\r
1069 mpx->font_check_sum[mpx->nfonts]=mpx_signed_quad(mpx);
\r
1070 @<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>;
\r
1071 n=mpx_get_byte(mpx); /* that is the area */
\r
1072 n=n+mpx_get_byte(mpx);
\r
1073 mpx->font_name[mpx->nfonts]=xmalloc(n+1,1);
\r
1075 mpx->font_name[mpx->nfonts][k]=mpx_get_byte(mpx);
\r
1076 mpx->font_name[mpx->nfonts][k]=0
\r
1078 @ The scaled size and design size are stored in \.{DVI} units divided by $2^{20}$.
\r
1079 The units for scaled size are a little different if we are reading a virtual
\r
1080 font, but this will be corrected when the scaled size is used. The scaled size
\r
1081 also needs to be truncated to at most 23 significant bits in order to make
\r
1082 the character width calculation match what \TeX\ does.
\r
1084 @<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>=
\r
1085 x=mpx_signed_quad(mpx);
\r
1087 while ( mpx->x>040000000 ) {
\r
1090 mpx->font_scaled_size[mpx->nfonts]=x*k/1048576.0;
\r
1091 if ( mpx->vf_reading )
\r
1092 mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)*mpx->dvi_per_fix/1048576.0;
\r
1093 else mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)/1048576.0;
\r
1096 double dvi_per_fix; /* converts points scaled $2^{20}$ to \.{DVI} units */
\r
1098 @ The |match_font| function tries to find a match for the font with internal
\r
1099 number~|ff|, returning |nf| or the number of the matching font. If
\r
1100 |exact=true|, the name and scaled size should match. Otherwise the scaled
\r
1101 size need not match but the font found must be already loaded, not just
\r
1104 @<Declare a function called |match_font|@>=
\r
1105 static integer mpx_match_font (MPX mpx, unsigned ff, boolean exact) {
\r
1106 unsigned f; /* font number being tested */
\r
1107 for (f=0; f<mpx->nfonts ; f++) {
\r
1109 @<Compare the names of fonts |f| and |ff|; |continue| if they differ@>;
\r
1111 if ( fabs(mpx->font_scaled_size[f]-mpx->font_scaled_size[ff])<= font_tolerance ) {
\r
1112 if ( ! mpx->vf_reading ) {
\r
1113 if ( mpx->local_only[f] ) {
\r
1114 mpx->font_num[f]=mpx->font_num[ff]; mpx->local_only[f]=false;
\r
1115 } else if ( mpx->font_num[f]!=mpx->font_num[ff] ) {
\r
1121 } else if ( mpx->info_base[f]!=max_widths ) {
\r
1126 if ( f<mpx->nfonts ) {
\r
1127 @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>;
\r
1132 @ @<Compare the names of fonts |f| and |ff|; |continue| if they differ@>=
\r
1133 if (strcmp(mpx->font_name[f],mpx->font_name[ff]))
\r
1136 @ @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>=
\r
1137 if ( fabs(mpx->font_design_size[f]-mpx->font_design_size[ff]) > font_tolerance ) {
\r
1138 font_error("Inconsistent design sizes given for ",ff);
\r
1139 @.Inconsistent design sizes@>
\r
1140 } else if ( mpx->font_check_sum[f]!=mpx->font_check_sum[ff] ) {
\r
1141 font_warn("Checksum mismatch for ", ff);
\r
1142 @.Checksum mismatch@>
\r
1145 @* Reading ordinary fonts.
\r
1146 An auxiliary array |in_width| is used to hold the widths as they are
\r
1147 input. The global variable |tfm_check_sum| is set to the check sum that
\r
1148 appears in the current \.{TFM} file.
\r
1151 integer in_width[256]; /* \.{TFM} width data in \.{DVI} units */
\r
1152 integer tfm_check_sum; /* check sum found in |tfm_file| */
\r
1154 @ Here is a procedure that absorbs the necessary information from a
\r
1155 \.{TFM} file, assuming that the file has just been successfully reset
\r
1156 so that we are ready to read its first byte. (A complete description of
\r
1157 \.{TFM} file format appears in the documentation of \.{TFtoPL} and will
\r
1158 not be repeated here.) The procedure does not check the \.{TFM} file
\r
1159 for validity, nor does it give explicit information about what is
\r
1160 wrong with a \.{TFM} file that proves to be invalid. The procedure simply
\r
1161 aborts the program if it detects anything amiss in the \.{TFM} data.
\r
1164 static void mpx_in_TFM (MPX mpx,integer f) {
\r
1165 /* input \.{TFM} data for font |f| or abort */
\r
1166 integer k; /* index for loops */
\r
1167 int lh; /* length of the header data, in four-byte words */
\r
1168 int nw; /* number of words in the width table */
\r
1169 int wp; /* new value of |info_ptr| after successful input */
\r
1170 @<Read past the header data; |abort| if there is a problem@>;
\r
1171 @<Store character-width indices at the end of the |width| table@>;
\r
1172 @<Read the width values into the |in_width| table@>;
\r
1173 @<Move the widths from |in_width| to |width|@>;
\r
1174 mpx->fbase[f]=0; mpx->ftop[f]=0;
\r
1179 @ @<Read past the header...@>=
\r
1180 mpx_read_tfm_word(mpx); lh=mpx->b2*(int)(256)+mpx->b3;
\r
1181 mpx_read_tfm_word(mpx);
\r
1182 mpx->font_bc[f]=mpx->b0*(int)(256)+mpx->b1;
\r
1183 mpx->font_ec[f]=mpx->b2*(int)(256)+mpx->b3;
\r
1184 if ( mpx->font_ec[f]<mpx->font_bc[f] ) mpx->font_bc[f]=mpx->font_ec[f]+1;
\r
1185 if ( mpx->info_ptr+mpx->font_ec[f]-mpx->font_bc[f]+1>max_widths )
\r
1186 mpx_abort(mpx,"DVItoMP capacity exceeded (width table size=%d)!",max_widths);
\r
1187 @.DVItoMP capacity exceeded...@>
\r
1188 wp=mpx->info_ptr+mpx->font_ec[f]-mpx->font_bc[f]+1;
\r
1189 mpx_read_tfm_word(mpx); nw=mpx->b0*256+mpx->b1;
\r
1190 if ( (nw==0)||(nw>256) )
\r
1191 font_abort("Bad TFM file for ",f);
\r
1193 for (k=1;k<=3+lh;k++) {
\r
1194 if ( feof(mpx->tfm_file) )
\r
1195 font_abort("Bad TFM file for ",f);
\r
1197 mpx_read_tfm_word(mpx);
\r
1199 if ( mpx->b0<128 )
\r
1200 mpx->tfm_check_sum=((mpx->b0*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
\r
1202 mpx->tfm_check_sum=(((mpx->b0-256)*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
\r
1206 @ @<Store character-width indices...@>=
\r
1208 for (k=mpx->info_ptr;k<=wp-1;k++ ) {
\r
1209 mpx_read_tfm_word(mpx);
\r
1210 if ( mpx->b0>nw )
\r
1211 font_abort("Bad TFM file for ",f);
\r
1213 mpx->width[k]=mpx->b0;
\r
1217 @ No fancy width calculation is needed here because \.{DVItoMP} stores
\r
1218 widths in their raw form as multiples of the design size scaled by $2^{20}$.
\r
1219 The |font_scaled_size| entries have been computed so that the final width
\r
1220 compution can be done in floating point if enough precision is available.
\r
1222 @<Read the width values into the |in_width| table@>=
\r
1223 for (k=0;k<=nw-1;k++) {
\r
1224 mpx_read_tfm_word(mpx);
\r
1225 if ( mpx->b0>127 ) mpx->b0=mpx->b0-256;
\r
1226 mpx->in_width[k]=((mpx->b0*0400+mpx->b1)*0400+mpx->b2)*0400+mpx->b3;
\r
1229 @ The width compution uses a scale factor |dvi_scale| that will be introduced
\r
1230 later. It is equal to one when not typesetting a character from a virtual
\r
1231 font. In that case, the following expressions do the width computation that is
\r
1232 so important in \.{DVItype}. It is less important here because it is impractical
\r
1233 to guarantee precise character positioning in \MP\ output. Nevertheless, the
\r
1234 width compution will be precise if reals have at least 46-bit mantissas and
\r
1235 |round(x-.5)| is equivalent to $\lfloor x\rfloor$. It may be a good idea to
\r
1236 modify this computation if these conditions are not met.
\r
1237 @^system dependencies@>
\r
1239 @<Width of character |c| in font |f|@>=
\r
1240 floor(mpx->dvi_scale*mpx->font_scaled_size[f]*char_width(f,c))
\r
1242 @ @<Width of character |p| in font |cur_font|@>=
\r
1243 floor(mpx->dvi_scale*mpx->font_scaled_size[cur_font]*char_width(cur_font,p))
\r
1245 @ @<Move the widths from |in_width| to |width|@>=
\r
1246 if ( mpx->in_width[0]!=0 )
\r
1247 font_abort("Bad TFM file for ",f); /* the first width should be zero */
\r
1249 mpx->info_base[f]=mpx->info_ptr-mpx->font_bc[f];
\r
1251 for (k=mpx->info_ptr;k<=wp-1;k++) {
\r
1252 mpx->width[k]=mpx->in_width[mpx->width[k]];
\r
1257 @* Reading virtual fonts.
\r
1259 The |in_VF| procedure absorbs the necessary information from a \.{VF} file that
\r
1260 has just been reset so that we are ready to read the first byte. (A complete
\r
1261 description of \.{VF} file format appears in the documention of \.{VFtoVP}).
\r
1262 Like |in_TFM|, this procedure simply aborts the program if it detects anything
\r
1263 wrong with the \.{VF} file.
\r
1266 @<Declare a function called |first_par|@>@;
\r
1267 static void mpx_in_VF (MPX mpx, integer f) {
\r
1268 /* read \.{VF} data for font |f| or abort */
\r
1269 integer p; /* a byte from the \.{VF} file */
\r
1270 boolean was_vf_reading; /* old value of |vf_reading| */
\r
1271 integer c; /* the current character code */
\r
1272 integer limit; /* space limitations force character codes to be less than this */
\r
1273 integer w; /* a \.{TFM} width being read */
\r
1274 was_vf_reading=mpx->vf_reading; mpx->vf_reading=true;
\r
1275 @<Start reading the preamble from a \.{VF} file@>;@/
\r
1276 @<Initialize the data structures for the virtual font@>;@/
\r
1277 p=mpx_get_byte(mpx);
\r
1278 while ( mpx->p>=fnt_def1 ) {
\r
1279 if ( mpx->p>fnt_def1+3 )
\r
1280 font_abort("Bad VF file for ",f);
\r
1281 mpx_define_font(mpx, mpx_first_par(mpx, p));
\r
1282 p=mpx_get_byte(mpx);
\r
1284 while ( p<=242 ) {
\r
1285 if ( feof(mpx->vf_file) )
\r
1286 font_abort("Bad VF file for ",f);
\r
1287 @<Read the packet length, character code, and \.{TFM} width@>;
\r
1288 @<Store the character packet in |cmd_buf|@>;
\r
1289 p=mpx_get_byte(mpx);
\r
1291 if ( mpx->p==post ) {
\r
1292 @<Finish setting up the data structures for the new virtual font@>;
\r
1293 mpx->vf_reading=was_vf_reading;
\r
1298 @ @<Start reading the preamble from a \.{VF} file@>=
\r
1299 p=mpx_get_byte(mpx);
\r
1301 font_abort("Bad VF file for ",f);
\r
1302 p=mpx_get_byte(mpx); /* fetch the identification byte */
\r
1304 font_abort("Bad VF file for ",f);
\r
1305 p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */
\r
1307 (void)mpx_get_byte(mpx);
\r
1308 mpx->tfm_check_sum=mpx_signed_quad(mpx);
\r
1309 (void)mpx_signed_quad(mpx); /* skip over the design size */
\r
1311 @ @<Initialize the data structures for the virtual font@>=
\r
1312 mpx->ftop[f]=mpx->vf_ptr;
\r
1313 if ( mpx->vf_ptr==mpx->nfonts )
\r
1314 mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums);
\r
1315 @.DVItoMP capacity exceeded...@>
\r
1316 decr(mpx->vf_ptr);
\r
1317 mpx->info_base[f]=mpx->info_ptr;
\r
1318 limit=max_widths-mpx->info_base[f];@/
\r
1319 mpx->font_bc[f]=limit; mpx->font_ec[f]=0
\r
1321 @ @<Read the packet length, character code, and \.{TFM} width@>=
\r
1323 p=mpx_signed_quad(mpx); c=mpx_signed_quad(mpx); w=mpx_signed_quad(mpx);
\r
1325 font_abort("Bad VF file for ",f);
\r
1327 c=mpx_get_byte(mpx); w=mpx_get_three_bytes(mpx);
\r
1330 mpx_abort(mpx,"DVItoMP capacity exceeded (max widths=%d)", max_widths);
\r
1331 @.DVItoMP capacity exceeded...@>
\r
1332 if ( c<mpx->font_bc[f] ) mpx->font_bc[f]=c;
\r
1333 if ( c>mpx->font_ec[f] ) mpx->font_ec[f]=c;
\r
1336 @ @<Store the character packet in |cmd_buf|@>=
\r
1337 if ( mpx->n_cmds+p>=virtual_space )
\r
1338 mpx_abort(mpx,"DVItoMP capacity exceeded (virtual font space=%d)",virtual_space);
\r
1339 @.DVItoMP capacity exceeded...@>
\r
1340 start_cmd(f,c)=mpx->n_cmds;
\r
1342 mpx->cmd_buf[mpx->n_cmds]=mpx_get_byte(mpx);
\r
1343 incr(mpx->n_cmds); decr(p);
\r
1345 mpx->cmd_buf[mpx->n_cmds]=eop; /* add the end-of-packet marker */
\r
1348 @ There are unused |width| and |cmd_ptr| entries if |font_bc[f]>0| but it isn't
\r
1349 worthwhile to slide everything down just to save a little space.
\r
1351 @<Finish setting up the data structures for the new virtual font@>=
\r
1352 mpx->fbase[f]=mpx->vf_ptr+1;
\r
1353 mpx->info_ptr=mpx->info_base[f]+mpx->font_ec[f]+1
\r
1358 The character width information for a font is loaded when the font is selected
\r
1359 for the first time. This information might already be loaded if the font has
\r
1360 already been used at a different scale factor. Otherwise, we look for a \.{VF}
\r
1361 file, or failing that, a \.{TFM} file. All this is done by the |select_font|
\r
1362 function that takes an external font number~|e| and returns the corresponding
\r
1363 internal font number with the width information loaded.
\r
1366 static integer mpx_select_font (MPX mpx, integer e) {
\r
1367 int f; /* the internal font number */
\r
1368 int ff; /* internal font number for an existing version */
\r
1369 integer k; /* general purpose loop counter */
\r
1370 @<Set |f| to the internal font number that corresponds to |e|,
\r
1371 or |abort| if there is none@>;
\r
1372 if ( mpx->info_base[f]==max_widths ) {
\r
1373 ff=mpx_match_font(mpx, f,false);
\r
1374 if ( ff<(int)mpx->nfonts ) {
\r
1375 @<Make font |f| refer to the width information from font |ff|@>;
\r
1377 @<Move the \.{VF} file name into the |cur_name| string@>;
\r
1378 if ( mpx_open_vf_file(mpx) ) {
\r
1379 mpx_in_VF(mpx, f);
\r
1381 if ( ! mpx_open_tfm_file(mpx) )
\r
1382 font_abort("No TFM file found for ",f);
\r
1383 @.no TFM file found@>
\r
1384 mpx_in_TFM(mpx, f);
\r
1386 @<Make sure the checksum in the font file matches the one given in the
\r
1387 |font_def| for font |f|@>;
\r
1389 @<Do any other initialization required for the new font |f|@>;
\r
1394 @ @<Set |f| to the internal font number that corresponds to |e|,...@>=
\r
1395 if ( mpx->cur_ftop<=mpx->nfonts )
\r
1396 mpx->cur_ftop=mpx->nfonts;
\r
1397 mpx->font_num[mpx->cur_ftop]=e;
\r
1399 while ((mpx->font_num[k]!=e)|| mpx->local_only[k] ) incr(k);
\r
1400 if ( k==(int)mpx->cur_ftop )
\r
1401 mpx_abort(mpx,"Undefined font selected");
\r
1402 f=mpx->internal_num[k]
\r
1404 @ @<Make font |f| refer to the width information from font |ff|@>=
\r
1406 mpx->font_bc[f]=mpx->font_bc[ff];
\r
1407 mpx->font_ec[f]=mpx->font_ec[ff];
\r
1408 mpx->info_base[f]=mpx->info_base[ff];
\r
1409 mpx->fbase[f]=mpx->fbase[ff];
\r
1410 mpx->ftop[f]=mpx->ftop[ff];
\r
1413 @ The string |cur_name| is supposed to be set to the external name of the
\r
1414 \.{VF} file for the current font.
\r
1415 @^system dependencies@>
\r
1417 @<Move the \.{VF} file name into the |cur_name| string@>=
\r
1418 mpx->cur_name = xstrdup (mpx->font_name[f])
\r
1420 @ @<Make sure the checksum in the font file matches the one given in the...@>=
\r
1422 if ( (mpx->font_check_sum[f]!=0)&&(mpx->tfm_check_sum!=0)&&@|
\r
1423 (mpx->font_check_sum[f]!=mpx->tfm_check_sum) ) {
\r
1424 font_warn("Checksum mismatch for ",f);
\r
1425 @.Checksum mismatch@>
\r
1429 @* Low level output routines.
\r
1431 One of the basic output operations is to write a \MP\ string expression for
\r
1432 a sequence of characters to be typeset. The main difficulties are that such
\r
1433 strings can contain arbitrary eight-bit bytes and there is no fixed limit on
\r
1434 the length of the string that needs to be produced. In extreme cases this
\r
1435 can lead to expressions such as
\r
1437 \hbox{\.{char7\&char15\&char31\&"?FWayzz"}}
\r
1438 \hbox{\.{\&"zzaF"\&char15\&char3\&char31}}
\r
1439 \hbox{\.{\&"Nxzzzzzzzwvtsqo"}}}
\r
1442 @ A global variable |state| keeps track of the output process.
\r
1443 When |state=normal| we have begun a quoted string and the next character
\r
1444 should be a printable character or a closing quote. When |state=special|
\r
1445 the last thing printed was a ``\.{char}'' construction or a closing quote
\r
1446 and an ampersand should come next. The starting condition |state=initial|
\r
1447 is a lot like |state=special|, except no ampersand is required.
\r
1449 @d special 0 /* the |state| after printing a ``\.{char}'' expression */
\r
1450 @d normal 1 /* the |state| value in a quoted string */
\r
1451 @d initial 2 /* initial |state| */
\r
1454 int state; /* controls the process of printing a string */
\r
1455 int print_col; /* there are at most this many characters on the current line */
\r
1457 @ @<Set initial values@>=
\r
1458 mpx->state = initial;
\r
1459 mpx->print_col = 0; /* there are at most this many characters on the current line */
\r
1461 @ To print a string on the \.{MPX} file, initialize |print_col|, ensure that
\r
1462 |state=initial|, and pass the characters one-at-a-time to |print_char|.
\r
1464 @<Declare subroutines for printing strings@>=
\r
1465 static void mpx_print_char (MPX mpx, unsigned char c) {
\r
1466 integer l; /* number of characters to print |c| or the \.{char} expression */
\r
1467 if ( printable(c) ) l=1;
\r
1468 else if ( c<10 ) l=5;
\r
1469 else if ( c<100 ) l=6;
\r
1471 if ( mpx->print_col+l>line_length-2 ) {
\r
1472 if ( mpx->state==normal ) {
\r
1473 fprintf(mpx->mpxfile,"\""); mpx->state=special;
\r
1475 fprintf(mpx->mpxfile,"\n");
\r
1478 @<Print |c| and update |state| and |print_col|@>;
\r
1481 @ @<Print |c| and update |state| and |print_col|@>=
\r
1482 if ( mpx->state==normal ) {
\r
1483 if ( printable(c) ) {
\r
1484 fprintf(mpx->mpxfile,"%c",xchr(c));
\r
1486 fprintf(mpx->mpxfile,"\"&char%d",c);
\r
1487 mpx->print_col +=2;
\r
1490 if ( mpx->state==special ) {
\r
1491 fprintf(mpx->mpxfile,"&");
\r
1492 incr(mpx->print_col);
\r
1494 if ( printable(c) ) {
\r
1495 fprintf(mpx->mpxfile,"\"%c",xchr(c));
\r
1496 incr(mpx->print_col);
\r
1498 fprintf(mpx->mpxfile,"char%d",c);
\r
1501 mpx->print_col += l;
\r
1502 if ( printable(c) )
\r
1503 mpx->state=normal;
\r
1505 mpx->state=special
\r
1507 @ The |end_char_string| procedure gets the string ended properly and ensures
\r
1508 that there is room for |l| more characters on the output line.
\r
1510 @<Declare subroutines for printing strings@>=
\r
1511 static void mpx_end_char_string (MPX mpx,integer l) {
\r
1512 while ( mpx->state>special ){
\r
1513 fprintf(mpx->mpxfile,"\"");
\r
1514 incr(mpx->print_col);
\r
1517 if ( mpx->print_col+l>line_length ) {
\r
1518 fprintf(mpx->mpxfile,"\n "); mpx->print_col=0;
\r
1520 mpx->state=initial; /* get ready to print the next string */
\r
1523 @ Since |end_char_string| resets |state:=initial|, all we have to do is set
\r
1524 |state:=initial| once at the beginning.
\r
1527 mpx->state=initial;
\r
1529 @ Characters and rules are positioned according to global variables |h| and~|v|
\r
1530 as will be explained later. We also need scale factors that convert quantities
\r
1531 to the right units when they are printed in the \.{MPX} file.
\r
1533 Even though all variable names in the \MP\ output are made local via \.{save}
\r
1534 commands, it is still desirable to preceed them with underscores. This makes
\r
1535 the output more likely to work when used in a macro definition, since the
\r
1536 generated variables names must not collide with formal parameters in such
\r
1541 integer v; /* the current position in \.{DVI} units */
\r
1542 double conv; /* converts \.{DVI} units to \MP\ points */
\r
1543 double mag; /* magnification factor times 1000 */
\r
1545 @ @c @<Declare a procedure called |finish_last_char|@>@;
\r
1546 static void mpx_do_set_char (MPX mpx,integer f, integer c) {
\r
1547 if ( (c<mpx->font_bc[f])||(c>mpx->font_ec[f]) )
\r
1548 mpx_abort(mpx,"attempt to typeset invalid character %d",c);
\r
1549 @.attempt to typeset...@>
\r
1550 if ((mpx->h!=mpx->str_h2)||(mpx->v!=mpx->str_v)||
\r
1551 (f!=mpx->str_f)||(mpx->dvi_scale!=mpx->str_scale) ) {
\r
1552 if ( mpx->str_f>=0 ) {
\r
1553 mpx_finish_last_char(mpx);
\r
1554 } else if ( ! mpx->fonts_used ) {
\r
1555 @<Prepare to output the first character on a page@>;
\r
1557 if ( ! mpx->font_used[f] )
\r
1558 @<Prepare to use font |f| for the first time on a page@>;
\r
1559 fprintf(mpx->mpxfile,"_s("); mpx->print_col=3;@/
\r
1560 mpx->str_scale=mpx->dvi_scale; mpx->str_f=f;
\r
1561 mpx->str_v=mpx->v; mpx->str_h1=mpx->h;
\r
1563 mpx_print_char(mpx, c);
\r
1564 mpx->str_h2=mpx->h+@<Width of character |c| in font |f|@>;
\r
1568 boolean font_used[(max_fonts+1)]; /* has this font been used on this page? */
\r
1569 boolean fonts_used; /* has any font been used on this page? */
\r
1570 boolean rules_used; /* has any rules been set on this page? */
\r
1572 integer str_v; /* starting position for current output string */
\r
1573 integer str_h2; /* where the current output string ends */
\r
1574 integer str_f; /* internal font number for the current output string */
\r
1575 double str_scale; /* value of |dvi_scale| for the current output string */
\r
1578 @ Before using any fonts we need to define a MetaPost macro for
\r
1579 typesetting character strings. The |font_used| array is not
\r
1580 initialized until it is actually time to output a character.
\r
1583 static void mpx_prepare_font_use(MPX mpx);
\r
1586 static void mpx_prepare_font_use(MPX mpx) {
\r
1588 for (k=0; k<mpx->nfonts;k++ )
\r
1589 mpx->font_used[k]=false;
\r
1590 mpx->fonts_used=true;
\r
1591 fprintf(mpx->mpxfile,"string _n[];\n");
\r
1592 fprintf(mpx->mpxfile,"vardef _s(expr _t,_f,_m,_x,_y)(text _c)=\n");
\r
1593 fprintf(mpx->mpxfile,
\r
1594 " addto _p also _t infont _f scaled _m shifted (_x,_y) _c; enddef;\n");
\r
1597 @ @<Prepare to output the first character on a page@>=
\r
1598 mpx_prepare_font_use(mpx)
\r
1601 @ @<Do any other initialization required for the new font |f|@>=
\r
1602 mpx->font_used[f]=false;
\r
1604 @ Do what is necessary when the font with internal number f is used for the
\r
1605 first time on a page.
\r
1608 static void mpx_first_use(MPX mpx, int f) ;
\r
1611 static void mpx_first_use(MPX mpx, int f) {
\r
1612 mpx->font_used[f]=true;
\r
1613 fprintf(mpx->mpxfile,"_n%d=",f);
\r
1615 mpx_print_font(mpx, f);
\r
1616 mpx_end_char_string(mpx, 1);
\r
1617 fprintf(mpx->mpxfile,";\n");
\r
1620 @ @<Prepare to use font |f| for the first time on a page@>=
\r
1621 mpx_first_use(mpx,f);
\r
1623 @ We maintain the invariant that |str_f=-1| when there is no output string
\r
1624 under construction.
\r
1626 @<Declare a procedure called |finish_last_char|@>=
\r
1627 static void mpx_finish_last_char (MPX mpx) {
\r
1629 /* font scale factor and \MP\ coordinates of reference point */
\r
1630 if ( mpx->str_f>=0 ) {
\r
1631 if (mpx->mode==mpx_tex_mode) {
\r
1632 m=mpx->str_scale*mpx->font_scaled_size[mpx->str_f]*
\r
1633 mpx->mag/mpx->font_design_size[mpx->str_f];
\r
1634 x=mpx->conv*mpx->str_h1;
\r
1635 y=mpx->conv*(-mpx->str_v);
\r
1636 if ( (fabs(x)>=4096.0)||(fabs(y)>=4096.0)||(m>=4096.0)||(m<0) ) {
\r
1637 mpx_warn(mpx,"text is out of range");
\r
1638 mpx_end_char_string(mpx, 60);
\r
1640 mpx_end_char_string(mpx, 40);
\r
1642 fprintf(mpx->mpxfile,",_n%d,%1.5f,%1.4f,%1.4f,",mpx->str_f,m,x,y);
\r
1643 @<Print a \.{withcolor} specifier if appropriate@>@/
\r
1644 fprintf(mpx->mpxfile,");\n");
\r
1646 m = mpx->str_size / mpx->font_design_size[mpx->str_f];
\r
1647 x = mpx->dmp_str_h1 * mpx->unit;
\r
1648 y = YCORR - mpx->dmp_str_v * mpx->unit;
\r
1649 if (fabs(x) >= 4096.0 || fabs(y) >= 4096.0 || m >= 4096.0 || m < 0) {
\r
1650 mpx_warn(mpx,"text out of range ignored");
\r
1651 mpx_end_char_string(mpx,67);
\r
1653 mpx_end_char_string(mpx,47);
\r
1655 fprintf(mpx->mpxfile, ")infont n%d", mpx->font_num[mpx->str_f]);
\r
1656 mpx_slant_and_ht(mpx);
\r
1657 fprintf(mpx->mpxfile, ",%.5f,%.4f,%.4f);\n", (m*1.00375), x, y);
\r
1663 @ Setting rules is fairly simple.
\r
1666 static void mpx_do_set_rule (MPX mpx,integer ht, integer wd) {
\r
1667 double xx1,yy1,xx2,yy2,ww;
\r
1668 /* \MP\ coordinates of lower-left and upper-right corners */
\r
1670 @<Handle a special rule that determines the box size@>
\r
1671 } else if ( (ht>0)||(wd>0) ) {
\r
1672 if ( mpx->str_f>=0 )
\r
1673 mpx_finish_last_char(mpx);
\r
1674 if ( ! mpx->rules_used ) {
\r
1675 mpx->rules_used=true;
\r
1676 fprintf(mpx->mpxfile,
\r
1677 "interim linecap:=0;\n"
\r
1678 "vardef _r(expr _a,_w)(text _t) =\n"
\r
1679 " addto _p doublepath _a withpen pencircle scaled _w _t enddef;");
\r
1681 @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke
\r
1682 and |ww| the desired stroke width@>;
\r
1683 if ( (fabs(xx1)>=4096.0)||(fabs(yy1)>=4096.0)||@|
\r
1684 (fabs(xx2)>=4096.0)||(fabs(yy2)>=4096.0)||(ww>=4096.0) )
\r
1685 mpx_warn(mpx,"hrule or vrule is out of range");
\r
1686 fprintf(mpx->mpxfile,"_r((%1.4f,%1.4f)..(%1.4f,%1.4f), %1.4f,",xx1,yy1,xx2,yy2,ww);
\r
1687 @<Print a \.{withcolor} specifier if appropriate@>@/
\r
1688 fprintf(mpx->mpxfile,");\n");
\r
1692 @ @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke...@>=
\r
1693 xx1=mpx->conv*mpx->h;
\r
1694 yy1=mpx->conv*(-mpx->v);
\r
1696 xx2=xx1+mpx->conv*wd;
\r
1701 yy2=yy1+mpx->conv*ht;
\r
1707 @ Rules of width one dvi unit are not typeset since \.{MPtoTeX} adds an
\r
1708 extraneous rule of this width in order to allow \.{DVItoMP} to deduce the
\r
1709 dimensions of the boxes it ships out. The box width is the left edge of the
\r
1710 last such rule; the height and depth are at the top and bottom of the rule.
\r
1711 There should be only one special rule per picture but there could be more if
\r
1712 the user tries to typeset his own one-dvi-unit rules. In this case the
\r
1713 dimension-determining rule is the last one in the picture.
\r
1715 @<Handle a special rule that determines the box size@>=
\r
1717 mpx->pic_wd=mpx->h; mpx->pic_dp=mpx->v; mpx->pic_ht=ht-mpx->v;
\r
1721 integer pic_dp; integer pic_ht; integer pic_wd; /* picture dimensions from special rule */
\r
1723 @ The following initialization and clean-up is required. We do a little more
\r
1724 initialization than is absolutely necessary since some compilers might complain
\r
1725 if the variables are uninitialized when |do_set_char| tests them.
\r
1728 static void mpx_start_picture (MPX mpx) {
\r
1729 mpx->fonts_used=false;
\r
1730 mpx->rules_used=false;
\r
1731 mpx->graphics_used=false;
\r
1735 mpx->str_scale=1.0; /* values don't matter */
\r
1736 mpx->dmp_str_v = 0.0;
\r
1737 mpx->dmp_str_h2 = 0.0;
\r
1738 mpx->str_size = 0.0;
\r
1739 fprintf(mpx->mpxfile,
\r
1740 "begingroup save %s_p,_r,_s,_n; picture _p; _p=nullpicture;\n",
\r
1741 (mpx->mode == mpx_tex_mode ? "" : "_C,_D,"));
\r
1744 static void mpx_stop_picture (MPX mpx) {
\r
1745 double w,h,dd; /* width, height, negative depth in PostScript points */
\r
1746 if ( mpx->str_f>=0 )
\r
1747 mpx_finish_last_char(mpx);
\r
1748 if (mpx->mode==mpx_tex_mode) {
\r
1749 @<Print a \&{setbounds} command based on picture dimensions@>;
\r
1751 fprintf(mpx->mpxfile,"_p endgroup\n");
\r
1754 @ @<Print a \&{setbounds} command based on picture dimensions@>=
\r
1755 dd=-mpx->pic_dp*mpx->conv;
\r
1756 w=mpx->conv*mpx->pic_wd;
\r
1757 h=mpx->conv*mpx->pic_ht;
\r
1758 fprintf(mpx->mpxfile,
\r
1759 "setbounds _p to (0,%1.4f)--(%1.4f,%1.4f)--\n"
\r
1760 " (%1.4f,%1.4f)--(0,%1.4f)--cycle;\n",dd,w,dd,w,h,h)
\r
1762 @* Translation to symbolic form.
\r
1764 The main work of \.{DVItoMP} is accomplished by the |do_dvi_commands|
\r
1765 procedure, which produces the output for an entire page, assuming that the
\r
1766 |bop| command for that page has already been processed. This procedure is
\r
1767 essentially an interpretive routine that reads and acts on the \.{DVI}
\r
1768 commands. It is also capable of executing the typesetting commands for
\r
1769 a character in a virtual font.
\r
1771 @ The definition of \.{DVI} files refers to six registers,
\r
1772 $(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units.
\r
1773 These units come directly from the input file except they need to be
\r
1774 rescaled when typesetting characters from a virtual font.
\r
1775 The stack of $(h,v,w,x,y,z)$ values is represented by six arrays
\r
1776 called |hstack|, \dots, |zstack|.
\r
1779 integer w;integer x;integer y;integer z;
\r
1780 /* current state values (|h| and |v| have already been declared) */
\r
1781 integer hstack[(stack_size+1)];
\r
1782 integer vstack[(stack_size+1)];
\r
1783 integer wstack[(stack_size+1)];
\r
1784 integer xstack[(stack_size+1)];
\r
1785 integer ystack[(stack_size+1)];
\r
1786 integer zstack[(stack_size+1)]; /* pushed down values in \.{DVI} units */
\r
1787 integer stk_siz; /* the current stack size */
\r
1788 double dvi_scale; /* converts units of current input source to \.{DVI} units */
\r
1790 @ @<Do initialization required before starting a new page@>=
\r
1791 mpx->dvi_scale=1.0;
\r
1793 mpx->h=0; mpx->v=0;
\r
1794 mpx->Xslant = 0.0; mpx->Xheight = 0.0
\r
1796 @ Next, we need procedures to handle |push| and |pop| commands.
\r
1798 @c @<Declare procedures to handle color commands@>
\r
1799 static void mpx_do_push (MPX mpx) {
\r
1800 if ( mpx->stk_siz==stack_size )
\r
1801 mpx_abort(mpx,"DVItoMP capacity exceeded (stack size=%d)",stack_size);
\r
1802 @.DVItoMP capacity exceeded...@>
\r
1803 mpx->hstack[mpx->stk_siz]=mpx->h;
\r
1804 mpx->vstack[mpx->stk_siz]=mpx->v; mpx->wstack[mpx->stk_siz]=mpx->w;
\r
1805 mpx->xstack[mpx->stk_siz]=mpx->x;
\r
1806 mpx->ystack[mpx->stk_siz]=mpx->y; mpx->zstack[mpx->stk_siz]=mpx->z;
\r
1807 incr(mpx->stk_siz);
\r
1810 static void mpx_do_pop (MPX mpx) {
\r
1811 if ( mpx->stk_siz==0 )
\r
1812 bad_dvi("attempt to pop empty stack");
\r
1814 decr(mpx->stk_siz);
\r
1815 mpx->h=mpx->hstack[mpx->stk_siz];
\r
1816 mpx->v=mpx->vstack[mpx->stk_siz]; mpx->w=mpx->wstack[mpx->stk_siz];
\r
1817 mpx->x=mpx->xstack[mpx->stk_siz];
\r
1818 mpx->y=mpx->ystack[mpx->stk_siz]; mpx->z=mpx->zstack[mpx->stk_siz];
\r
1822 @ The |set_virtual_char| procedure is mutually recursive with
\r
1823 |do_dvi_commands|. This is really a supervisory
\r
1825 procedure that calls |do_set_char| or adjusts the input source to read
\r
1826 typesetting commands for a character in a virtual font.
\r
1829 static void mpx_do_dvi_commands (MPX mpx);
\r
1830 static void mpx_set_virtual_char (MPX mpx,integer f, integer c) {
\r
1831 double old_scale; /* original value of |dvi_scale| */
\r
1832 int old_buf_ptr; /* original value of the input pointer |buf_ptr| */
\r
1833 int old_fbase,old_ftop; /* originally applicable part of the |font_num| table */
\r
1834 if ( mpx->fbase[f]==0 )
\r
1835 mpx_do_set_char(mpx, f,c);
\r
1837 old_fbase=mpx->cur_fbase; old_ftop=mpx->cur_ftop;
\r
1838 mpx->cur_fbase=mpx->fbase[f];
\r
1839 mpx->cur_ftop=mpx->ftop[f];
\r
1840 old_scale=mpx->dvi_scale;
\r
1841 mpx->dvi_scale=mpx->dvi_scale*mpx->font_scaled_size[f];
\r
1842 old_buf_ptr=mpx->buf_ptr;
\r
1843 mpx->buf_ptr=start_cmd(f,c);
\r
1845 mpx_do_dvi_commands(mpx);
\r
1846 mpx_do_pop(mpx);@/
\r
1847 mpx->buf_ptr=old_buf_ptr;
\r
1848 mpx->dvi_scale=old_scale;
\r
1849 mpx->cur_fbase=old_fbase;
\r
1850 mpx->cur_ftop=old_ftop;
\r
1854 @ Before we get into the details of |do_dvi_commands|, it is convenient to
\r
1855 consider a simpler routine that computes the first parameter of each
\r
1858 @d four_cases(A) (A): case (A)+1: case (A)+2: case (A)+3
\r
1859 @d eight_cases(A) four_cases((A)): case four_cases((A)+4)
\r
1860 @d sixteen_cases(A) eight_cases((A)): case eight_cases((A)+8)
\r
1861 @d thirty_two_cases(A) sixteen_cases((A)): case sixteen_cases((A)+16)
\r
1862 @d sixty_four_cases(A) thirty_two_cases((A)): case thirty_two_cases((A)+32)
\r
1864 @<Declare a function called |first_par|@>=
\r
1865 static integer mpx_first_par (MPX mpx, unsigned int o) {
\r
1867 case sixty_four_cases(set_char_0):
\r
1868 case sixty_four_cases(set_char_0+64):
\r
1869 return (o-set_char_0);
\r
1871 case set1: case put1: case fnt1: case xxx1: case fnt_def1:
\r
1872 return mpx_get_byte(mpx);
\r
1874 case set1+1: case put1+1: case fnt1+1: case xxx1+1: case fnt_def1+1:
\r
1875 return mpx_get_two_bytes(mpx);
\r
1877 case set1+2: case put1+2: case fnt1+2: case xxx1+2: case fnt_def1+2:
\r
1878 return mpx_get_three_bytes(mpx);
\r
1880 case right1: case w1: case x1: case down1: case y1: case z1:
\r
1881 return mpx_signed_byte(mpx);
\r
1883 case right1+1: case w1+1: case x1+1: case down1+1: case y1+1: case z1+1:
\r
1884 return mpx_signed_pair(mpx);
\r
1886 case right1+2: case w1+2: case x1+2: case down1+2: case y1+2: case z1+2:
\r
1887 return mpx_signed_trio(mpx);
\r
1889 case set1+3: case set_rule: case put1+3: case put_rule:
\r
1890 case right1+3: case w1+3: case x1+3: case down1+3: case y1+3: case z1+3:
\r
1891 case fnt1+3: case xxx1+3: case fnt_def1+3:
\r
1892 return mpx_signed_quad(mpx);
\r
1894 case nop: case bop: case eop: case push: case pop: case pre: case post:
\r
1895 case post_post: case undefined_commands:
\r
1898 case w0: return mpx->w; break;
\r
1899 case x0: return mpx->x; break;
\r
1900 case y0: return mpx->y; break;
\r
1901 case z0: return mpx->z; break;
\r
1902 case sixty_four_cases(fnt_num_0):
\r
1903 return (o-fnt_num_0);
\r
1906 return 0; /* compiler warning */
\r
1909 @ Here is the |do_dvi_commands| procedure.
\r
1912 static void mpx_do_dvi_commands (MPX mpx) {
\r
1913 unsigned int o; /* operation code of the current command */
\r
1914 integer p,q; /* parameters of the current command */
\r
1915 integer cur_font; /* current internal font number */
\r
1916 if ( (mpx->cur_fbase<mpx->cur_ftop) && (mpx->buf_ptr<virtual_space) )
\r
1917 cur_font=mpx_select_font(mpx, mpx->font_num[mpx->cur_ftop-1]); /* select first local font */
\r
1919 cur_font=max_fnums+1; /* current font is undefined */
\r
1920 mpx->w=0; mpx->x=0; mpx->y=0; mpx->z=0; /* initialize the state variables */
\r
1922 @<Translate the next command in the \.{DVI} file; |return| if it was |eop|@>;
\r
1926 @ The multiway switch in |first_par|, above, was organized by the length
\r
1927 of each command; the one in |do_dvi_commands| is organized by the semantics.
\r
1929 @ @<Translate the next command...@>=
\r
1931 o=mpx_get_byte(mpx);
\r
1932 p=mpx_first_par(mpx, o);
\r
1933 if ( feof(mpx->dvi_file) )
\r
1934 bad_dvi("the DVI file ended prematurely");
\r
1935 @.the DVI file ended prematurely@>
\r
1936 if ( o<set1+4 ) { /* |set_char_0| through |set_char_127|, |set1| through |set4| */
\r
1937 if ( cur_font>max_fnums ) {
\r
1938 if ( mpx->vf_reading )
\r
1939 mpx_abort(mpx,"no font selected for character %d in virtual font", p);
\r
1941 bad_dvi_two("no font selected for character %d",p);
\r
1943 @.no font selected@>
\r
1944 mpx_set_virtual_char(mpx, cur_font,p);
\r
1945 mpx->h += @<Width of character |p| in font |cur_font|@>;
\r
1948 case four_cases(put1):
\r
1949 mpx_set_virtual_char(mpx, cur_font, p);
\r
1952 q=trunc(mpx_signed_quad(mpx)*mpx->dvi_scale);
\r
1953 mpx_do_set_rule(mpx, trunc(p*mpx->dvi_scale),q);
\r
1957 q=trunc(mpx_signed_quad(mpx)*mpx->dvi_scale);
\r
1958 mpx_do_set_rule(mpx, trunc(p*mpx->dvi_scale),q);
\r
1960 @<Additional cases for translating \.{DVI} command |o| with
\r
1961 first parameter |p|@>@;
\r
1962 case undefined_commands:
\r
1963 bad_dvi_two("undefined command %d",o);
\r
1964 @.undefined command@>
\r
1966 } /* all cases have been enumerated */
\r
1970 @ @<Additional cases for translating \.{DVI} command |o|...@>=
\r
1971 case four_cases(xxx1):
\r
1972 mpx_do_xxx(mpx, p);
\r
1974 case pre: case post: case post_post:
\r
1975 bad_dvi("preamble or postamble within a page!");
\r
1976 @.preamble or postamble within a page@>
\r
1979 @ @<Additional cases for translating \.{DVI} command |o|...@>=
\r
1983 bad_dvi("bop occurred before eop");
\r
1984 @.bop occurred before eop@>
\r
1987 if ( mpx->stk_siz!=0 )
\r
1988 bad_dvi("stack not empty at end of page");
\r
1989 @.stack not empty...@>
\r
1999 @ @<Additional cases for translating \.{DVI} command |o|...@>=
\r
2000 case four_cases(right1):
\r
2001 mpx->h += trunc(p*mpx->dvi_scale);
\r
2003 case w0: case four_cases(w1):
\r
2004 mpx->w = trunc(p*mpx->dvi_scale); mpx->h += mpx->w;
\r
2006 case x0: case four_cases(x1):
\r
2007 mpx->x = trunc(p*mpx->dvi_scale); mpx->h += mpx->x;
\r
2009 case four_cases(down1):
\r
2010 mpx->v += trunc(p*mpx->dvi_scale);
\r
2012 case y0: case four_cases(y1):
\r
2013 mpx->y = trunc(p*mpx->dvi_scale); mpx->v += mpx->y;
\r
2015 case z0: case four_cases(z1):
\r
2016 mpx->z = trunc(p*mpx->dvi_scale); mpx->v += mpx->z;
\r
2019 @ @<Additional cases for translating \.{DVI} command |o|...@>=
\r
2020 case sixty_four_cases(fnt_num_0): case four_cases(fnt1):
\r
2021 cur_font = mpx_select_font(mpx, p);
\r
2023 case four_cases(fnt_def1):
\r
2024 mpx_define_font(mpx, p);
\r
2027 @* The main program.
\r
2028 Now we are ready to put it all together. This is where \.{DVItoMP} starts,
\r
2029 and where it ends.
\r
2032 static int mpx_dvitomp (MPX mpx, char *dviname) {
\r
2034 mpx->dviname = dviname;
\r
2035 mpx_open_dvi_file(mpx);
\r
2036 @<Process the preamble@>;
\r
2037 mpx_open_mpxfile(mpx);
\r
2038 fprintf (mpx->mpxfile,"%s\n",mpx->banner);
\r
2040 @<Advance to the next |bop| command@>;
\r
2041 for (k=0;k<=10;k++)
\r
2042 (void)mpx_signed_quad(mpx);
\r
2043 @<Do initialization required before starting a new page@>;
\r
2044 mpx_start_picture(mpx);
\r
2045 mpx_do_dvi_commands(mpx);
\r
2046 mpx_stop_picture(mpx);
\r
2047 fprintf(mpx->mpxfile,"mpxbreak\n");
\r
2049 if ( mpx->history<=mpx_cksum_trouble )
\r
2052 return mpx->history;
\r
2055 @ The main program needs a few global variables in order to do its work.
\r
2058 integer k;integer p; /* general purpose registers */
\r
2059 integer numerator;integer denominator; /* stated conversion ratio */
\r
2061 @ @<Process the preamble@>=
\r
2064 p=mpx_get_byte(mpx); /* fetch the first byte */
\r
2066 bad_dvi("First byte isn""t start of preamble!");
\r
2067 @.First byte isn't...@>
\r
2068 p=mpx_get_byte(mpx); /* fetch the identification byte */
\r
2070 mpx_warn(mpx,"identification in byte 1 should be %d!", id_byte);
\r
2071 @.identification...should be n@>
\r
2072 @<Compute the conversion factor@>;
\r
2073 p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */
\r
2076 (void)mpx_get_byte(mpx);
\r
2080 @ The conversion factor |conv| is figured as follows: There are exactly
\r
2081 |n/d| decimicrons per \.{DVI} unit, and 254000 decimicrons per inch,
\r
2082 and |resolution| pixels per inch. Then we have to adjust this
\r
2083 by the stated amount of magnification. No such adjustment is needed for
\r
2084 |dvi_per_fix| since it is used to convert design sizes.
\r
2086 @<Compute the conversion factor@>=
\r
2087 mpx->numerator=mpx_signed_quad(mpx); mpx->denominator=mpx_signed_quad(mpx);
\r
2088 if ( (mpx->numerator<=0)||(mpx->denominator<=0) )
\r
2089 bad_dvi("bad scale ratio in preamble");
\r
2090 @.bad scale ratio@>
\r
2091 mpx->mag=mpx_signed_quad(mpx)/1000.0;
\r
2092 if ( mpx->mag<=0.0 )
\r
2093 bad_dvi("magnification isn't positive");
\r
2094 @.magnification isn't positive@>
\r
2095 mpx->conv=(mpx->numerator/254000.0)*(72.0/mpx->denominator)*mpx->mag;
\r
2096 mpx->dvi_per_fix=(254000.0/mpx->numerator)*(mpx->denominator/72.27)/1048576.0;
\r
2098 @ @<Advance to the next |bop| command@>=
\r
2101 k=mpx_get_byte(mpx);
\r
2102 if ( (k>=fnt_def1)&&(k<fnt_def1+4) ){
\r
2103 p=mpx_first_par(mpx, k);
\r
2104 mpx_define_font(mpx, p); k=nop;
\r
2110 bad_dvi("missing bop");
\r
2114 @ Global filenames.
\r
2120 These changes support \.{dvips}-style ``\.{color push NAME}'' and
\r
2121 ``\.{color pop}'' specials. We store a list of named colors, sorted by
\r
2122 name, and decorate the relevant drawing commands with ``\.{withcolor
\r
2123 (r,g,b)}'' specifiers while a color is defined.
\r
2125 @ A constant bounding the size of the named-color array.
\r
2127 @d max_named_colors 100 /* maximum number of distinct named colors */
\r
2129 @ Then we declare a record for color types.
\r
2132 typedef struct named_color_record {
\r
2133 char *name; /* color name */
\r
2134 char *value; /* text to pass to MetaPost */
\r
2135 } named_color_record;
\r
2137 @ Declare the named-color array itself.
\r
2140 named_color_record named_colors[(max_named_colors+1)];
\r
2141 /* stores information about named colors, in sorted order by name */
\r
2142 integer num_named_colors; /* number of elements of |named_colors| that are valid */
\r
2144 @ This function, used only during initialization, defines a named color.
\r
2147 static void mpx_def_named_color (MPX mpx, char *n, char *v) {
\r
2148 mpx->num_named_colors++;
\r
2149 assert(mpx->num_named_colors<max_named_colors);
\r
2150 mpx->named_colors[mpx->num_named_colors].name = n;
\r
2151 mpx->named_colors[mpx->num_named_colors].value = v;
\r
2154 @ @<Declarations@>=
\r
2155 static void mpx_def_named_color (MPX mpx, char *n, char *v);
\r
2157 @ During the initialization phase, we define values for all the named
\r
2158 colors defined in \.{colordvi.tex}. CMYK-to-RGB conversion by GhostScript.
\r
2160 This list has to be sorted alphabetically!
\r
2162 @<Set initial values@>=
\r
2163 mpx->num_named_colors = 0;
\r
2164 mpx_def_named_color(mpx, "Apricot", "(1.0, 0.680006, 0.480006)");
\r
2165 mpx_def_named_color(mpx, "Aquamarine", "(0.180006, 1.0, 0.7)");
\r
2166 mpx_def_named_color(mpx, "Bittersweet", "(0.760012, 0.0100122, 0.0)");
\r
2167 mpx_def_named_color(mpx, "Black", "(0.0, 0.0, 0.0)");
\r
2168 mpx_def_named_color(mpx, "Blue", "(0.0, 0.0, 1.0)");
\r
2169 mpx_def_named_color(mpx, "BlueGreen", "(0.15, 1.0, 0.669994)");
\r
2170 mpx_def_named_color(mpx, "BlueViolet", "(0.1, 0.05, 0.960012)");
\r
2171 mpx_def_named_color(mpx, "BrickRed", "(0.719994, 0.0, 0.0)");
\r
2172 mpx_def_named_color(mpx, "Brown", "(0.4, 0.0, 0.0)");
\r
2173 mpx_def_named_color(mpx, "BurntOrange", "(1.0, 0.489988, 0.0)");
\r
2174 mpx_def_named_color(mpx, "CadetBlue", "(0.380006, 0.430006, 0.769994)");
\r
2175 mpx_def_named_color(mpx, "CarnationPink", "(1.0, 0.369994, 1.0)");
\r
2176 mpx_def_named_color(mpx, "Cerulean", "(0.0600122, 0.889988, 1.0)");
\r
2177 mpx_def_named_color(mpx, "CornflowerBlue", "(0.35, 0.869994, 1.0)");
\r
2178 mpx_def_named_color(mpx, "Cyan", "(0.0, 1.0, 1.0)");
\r
2179 mpx_def_named_color(mpx, "Dandelion", "(1.0, 0.710012, 0.160012)");
\r
2180 mpx_def_named_color(mpx, "DarkOrchid", "(0.6, 0.2, 0.8)");
\r
2181 mpx_def_named_color(mpx, "Emerald", "(0.0, 1.0, 0.5)");
\r
2182 mpx_def_named_color(mpx, "ForestGreen", "(0.0, 0.880006, 0.0)");
\r
2183 mpx_def_named_color(mpx, "Fuchsia", "(0.45, 0.00998169, 0.919994)");
\r
2184 mpx_def_named_color(mpx, "Goldenrod", "(1.0, 0.9, 0.160012)");
\r
2185 mpx_def_named_color(mpx, "Gray", "(0.5, 0.5, 0.5)");
\r
2186 mpx_def_named_color(mpx, "Green", "(0.0, 1.0, 0.0)");
\r
2187 mpx_def_named_color(mpx, "GreenYellow", "(0.85, 1.0, 0.310012)");
\r
2188 mpx_def_named_color(mpx, "JungleGreen", "(0.0100122, 1.0, 0.480006)");
\r
2189 mpx_def_named_color(mpx, "Lavender", "(1.0, 0.519994, 1.0)");
\r
2190 mpx_def_named_color(mpx, "LimeGreen", "(0.5, 1.0, 0.0)");
\r
2191 mpx_def_named_color(mpx, "Magenta", "(1.0, 0.0, 1.0)");
\r
2192 mpx_def_named_color(mpx, "Mahogany", "(0.65, 0.0, 0.0)");
\r
2193 mpx_def_named_color(mpx, "Maroon", "(0.680006, 0.0, 0.0)");
\r
2194 mpx_def_named_color(mpx, "Melon", "(1.0, 0.539988, 0.5)");
\r
2195 mpx_def_named_color(mpx, "MidnightBlue", "(0.0, 0.439988, 0.569994)");
\r
2196 mpx_def_named_color(mpx, "Mulberry", "(0.640018, 0.0800061, 0.980006)");
\r
2197 mpx_def_named_color(mpx, "NavyBlue", "(0.0600122, 0.460012, 1.0)");
\r
2198 mpx_def_named_color(mpx, "OliveGreen", "(0.0, 0.6, 0.0)");
\r
2199 mpx_def_named_color(mpx, "Orange", "(1.0, 0.389988, 0.130006)");
\r
2200 mpx_def_named_color(mpx, "OrangeRed", "(1.0, 0.0, 0.5)");
\r
2201 mpx_def_named_color(mpx, "Orchid", "(0.680006, 0.360012, 1.0)");
\r
2202 mpx_def_named_color(mpx, "Peach", "(1.0, 0.5, 0.3)");
\r
2203 mpx_def_named_color(mpx, "Periwinkle", "(0.430006, 0.45, 1.0)");
\r
2204 mpx_def_named_color(mpx, "PineGreen", "(0.0, 0.75, 0.160012)");
\r
2205 mpx_def_named_color(mpx, "Plum", "(0.5, 0.0, 1.0)");
\r
2206 mpx_def_named_color(mpx, "ProcessBlue", "(0.0399878, 1.0, 1.0)");
\r
2207 mpx_def_named_color(mpx, "Purple", "(0.55, 0.139988, 1.0)");
\r
2208 mpx_def_named_color(mpx, "RawSienna", "(0.55, 0.0, 0.0)");
\r
2209 mpx_def_named_color(mpx, "Red", "(1.0, 0.0, 0.0)");
\r
2210 mpx_def_named_color(mpx, "RedOrange", "(1.0, 0.230006, 0.130006)");
\r
2211 mpx_def_named_color(mpx, "RedViolet", "(0.590018, 0.0, 0.660012)");
\r
2212 mpx_def_named_color(mpx, "Rhodamine", "(1.0, 0.180006, 1.0)");
\r
2213 mpx_def_named_color(mpx, "RoyalBlue", "(0.0, 0.5, 1.0)");
\r
2214 mpx_def_named_color(mpx, "RoyalPurple", "(0.25, 0.1, 1.0)");
\r
2215 mpx_def_named_color(mpx, "RubineRed", "(1.0, 0.0, 0.869994)");
\r
2216 mpx_def_named_color(mpx, "Salmon", "(1.0, 0.469994, 0.619994)");
\r
2217 mpx_def_named_color(mpx, "SeaGreen", "(0.310012, 1.0, 0.5)");
\r
2218 mpx_def_named_color(mpx, "Sepia", "(0.3, 0.0, 0.0)");
\r
2219 mpx_def_named_color(mpx, "SkyBlue", "(0.380006, 1.0, 0.880006)");
\r
2220 mpx_def_named_color(mpx, "SpringGreen", "(0.739988, 1.0, 0.239988)");
\r
2221 mpx_def_named_color(mpx, "Tan", "(0.860012, 0.580006, 0.439988)");
\r
2222 mpx_def_named_color(mpx, "TealBlue", "(0.119994, 0.980006, 0.640018)");
\r
2223 mpx_def_named_color(mpx, "Thistle", "(0.880006, 0.410012, 1.0)");
\r
2224 mpx_def_named_color(mpx, "Turquoise", "(0.15, 1.0, 0.8)");
\r
2225 mpx_def_named_color(mpx, "Violet", "(0.210012, 0.119994, 1.0)");
\r
2226 mpx_def_named_color(mpx, "VioletRed", "(1.0, 0.189988, 1.0)");
\r
2227 mpx_def_named_color(mpx, "White", "(1.0, 1.0, 1.0)");
\r
2228 mpx_def_named_color(mpx, "WildStrawberry", "(1.0, 0.0399878, 0.610012)");
\r
2229 mpx_def_named_color(mpx, "Yellow", "(1.0, 1.0, 0.0)");
\r
2230 mpx_def_named_color(mpx, "YellowGreen", "(0.560012, 1.0, 0.260012)");
\r
2231 mpx_def_named_color(mpx, "YellowOrange", "(1.0, 0.580006, 0.0)");
\r
2233 @ Color commands get a separate warning procedure. |warn| sets |history :=
\r
2234 mpx_warning_given|, which causes a nonzero exit status; but color errors are
\r
2235 trivial and should leave the exit status zero.
\r
2237 @d color_warn(A) mpx_warn(mpx,A)
\r
2238 @d color_warn_two(A,B) mpx_warn(mpx,"%s%s",A,B)
\r
2240 @ The |do_xxx| procedure handles DVI specials (defined with the |xxx1...xxx4| commands).
\r
2244 @<Declare procedures to handle color commands@>=
\r
2245 static void mpx_do_xxx (MPX mpx, integer p)
\r
2247 unsigned char buf[(XXX_BUF+1)]; /* FIXME: Fixed size buffer. */
\r
2248 integer l, r, m, k, len;
\r
2250 int bufsiz = XXX_BUF;
\r
2252 while ( ( p > 0) && (len < bufsiz) ) {
\r
2253 buf[len] = mpx_get_byte(mpx);
\r
2254 decr(p); incr(len);
\r
2256 @<Check whether |buf| contains a color command; if not, |goto XXXX|@>
\r
2258 color_warn("long \"color\" special ignored");
\r
2261 if ( @<|buf| contains a color pop command@> ) {
\r
2262 @<Handle a color pop command@>
\r
2263 } else if ( @<|buf| contains a color push command@> ) {
\r
2264 @<Handle a color push command@>
\r
2266 color_warn("unknown \"color\" special ignored");
\r
2270 for (k = 1;k<=p;k++) (void)mpx_get_byte(mpx);
\r
2275 @<Check whether |buf| contains a color command; if not, |goto XXXX|@>=
\r
2277 || (buf[0] != 'c')
\r
2278 || (buf[1] != 'o')
\r
2279 || (buf[2] != 'l')
\r
2280 || (buf[3] != 'o')
\r
2281 || (buf[4] != 'r')
\r
2282 || (buf[5] != ' ')
\r
2285 @ @<|buf| contains a color push command@>=
\r
2287 (buf[6] == 'p') &&
\r
2288 (buf[7] == 'u') &&
\r
2289 (buf[8] == 's') &&
\r
2290 (buf[9] == 'h') &&
\r
2293 @ @<|buf| contains a color pop command@>=
\r
2295 (buf[6] == 'p') &&
\r
2296 (buf[7] == 'o') &&
\r
2299 @ The \.{color push} and \.{pop} commands imply a color stack, so we need a
\r
2300 global variable to hold that stack.
\r
2302 @d max_color_stack_depth 10 /* maximum depth of saved color stack */
\r
2304 @ Here's the actual stack variables.
\r
2307 integer color_stack_depth; /* current depth of saved color stack */
\r
2308 char *color_stack[(max_color_stack_depth+1)]; /* saved color stack */
\r
2310 @ Initialize the stack to empty.
\r
2312 @<Set initial values@>=
\r
2313 mpx->color_stack_depth = 0;
\r
2315 @ \.{color pop} just pops the stack.
\r
2317 @<Handle a color pop command@>=
\r
2318 mpx_finish_last_char(mpx);
\r
2319 if (mpx->color_stack_depth > 0 ) {
\r
2320 free(mpx->color_stack[mpx->color_stack_depth]);
\r
2321 decr(mpx->color_stack_depth);
\r
2323 color_warn("color stack underflow");
\r
2326 @ \.{color push} pushes a color onto the stack.
\r
2328 @<Handle a color push command@>=
\r
2329 mpx_finish_last_char(mpx);
\r
2330 if ( mpx->color_stack_depth >= max_color_stack_depth )
\r
2331 mpx_abort(mpx,"color stack overflow");
\r
2332 incr(mpx->color_stack_depth);
\r
2333 /* I don't know how to do string operations in Pascal. */
\r
2334 /* Skip over extra spaces after 'color push'. */
\r
2336 while ( (l < len - 1) && (buf[l] == ' ') ) incr(l);
\r
2337 if ( @<|buf[l]| contains an rgb command@> ) {
\r
2338 @<Handle a color push rgb command@>
\r
2339 } else if ( @<|buf[l]| contains a cmyk command@> ) {
\r
2340 @<Handle a color push cmyk command@>
\r
2341 } else if ( @<|buf[l]| contains a gray command@> ) {
\r
2342 @<Handle a color push gray command@>
\r
2344 @<Handle a named color push command@>
\r
2347 @ @<|buf[l]| contains an rgb command@>=
\r
2349 && (buf[l] == 'r')
\r
2350 && (buf[l+1] == 'g')
\r
2351 && (buf[l+2] == 'b')
\r
2352 && (buf[l+3] == ' ')
\r
2354 @ @<Handle a color push rgb command@>=
\r
2356 while ( (l < len) && (buf[l] == ' ') ) incr(l); /* Remove spaces at end of buf */
\r
2357 while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
\r
2358 mpx->color_stack[mpx->color_stack_depth]=xmalloc(len-l+3,1);
\r
2360 @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
\r
2362 @ @<|buf[l]| contains a gray command@>=
\r
2364 && (buf[l] == 'g')
\r
2365 && (buf[l+1] == 'r')
\r
2366 && (buf[l+2] == 'a')
\r
2367 && (buf[l+3] == 'y')
\r
2368 && (buf[l+4] == ' ')
\r
2370 @ @<Handle a color push gray command@>=
\r
2372 while ( (l < len) && (buf[l] == ' ') ) incr(l); /* Remove spaces at end of buf */
\r
2373 while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
\r
2374 mpx->color_stack[mpx->color_stack_depth]=xmalloc(len-l+9,1);
\r
2375 strcpy(mpx->color_stack[mpx->color_stack_depth],"white*");
\r
2377 @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
\r
2379 @ @<|buf[l]| contains a cmyk command@>=
\r
2381 && (buf[l] == 'c')
\r
2382 && (buf[l+1] == 'm')
\r
2383 && (buf[l+2] == 'y')
\r
2384 && (buf[l+3] == 'k')
\r
2385 && (buf[l+4] == ' ')
\r
2387 @ @<Handle a color push cmyk command@>=
\r
2389 while ( (l < len) && (buf[l] == ' ') ) incr(l);
\r
2390 /* Remove spaces at end of buf */
\r
2391 while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
\r
2392 mpx->color_stack[mpx->color_stack_depth]=xmalloc(len-l+7,1);
\r
2393 strcpy(mpx->color_stack[mpx->color_stack_depth],"cmyk");
\r
2395 @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
\r
2397 @ @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>=
\r
2398 mpx->color_stack[mpx->color_stack_depth][k] = '(';
\r
2400 while ( l < len ) {
\r
2401 if ( buf[l] == ' ' ) {
\r
2402 mpx->color_stack[mpx->color_stack_depth][k] = ',';
\r
2403 while ( (l < len) && (buf[l] == ' ') ) incr(l);
\r
2406 mpx->color_stack[mpx->color_stack_depth][k] = buf[l];
\r
2411 mpx->color_stack[mpx->color_stack_depth][k] = ')';
\r
2412 mpx->color_stack[mpx->color_stack_depth][k+1] = 0;
\r
2414 @ Binary-search the |named_colors| array, then push the found color onto
\r
2417 @<Handle a named color push command@>=
\r
2418 for (k = l;k<=len - 1;k++) {
\r
2419 buf[k - l] = xchr(buf[k]);
\r
2423 l = 1; r = mpx->num_named_colors;
\r
2425 while ( (l <= r) && ! found ) {
\r
2426 m = (l + r) / 2; k = strcmp((char *)(buf), mpx->named_colors[m].name);
\r
2428 mpx->color_stack[mpx->color_stack_depth]=xstrdup(mpx->named_colors[m].value);
\r
2430 } else if ( k < 0 ) {
\r
2437 color_warn_two("non-hardcoded color \"%s\" in \"color push\" command", buf);
\r
2438 mpx->color_stack[mpx->color_stack_depth]=xstrdup((char *)(buf));
\r
2441 @ Last but not least, this code snippet prints a \.{withcolor} specifier
\r
2442 for the top of the color stack, if the stack is nonempty.
\r
2444 @<Print a \.{withcolor} specifier if appropriate@>=
\r
2445 if ( mpx->color_stack_depth > 0 ) {
\r
2446 fprintf(mpx->mpxfile," withcolor %s\n",mpx->color_stack[mpx->color_stack_depth]);
\r
2452 This program reads device-independent troff output files,
\r
2453 and converts them into a symbolic form understood by MetaPost. Some
\r
2454 of the code was borrowed from DVItoMP. It understands all the D? graphics
\r
2455 functions that dpost does but it ignores `x X' device control functions
\r
2456 such as `x X SetColor:...', `x X BeginPath:', and `x X DrawPath:...'.
\r
2458 The output file is a sequence of MetaPost picture expressions, one for every
\r
2459 page in the input file. It makes no difference where the input file comes
\r
2460 from, but it is intended to process the result of running eqn and troff on
\r
2461 the output of MPtoTR. Such a file contains one page for every btex...etex
\r
2462 block in the original input. This program then creates a corresponding
\r
2463 sequence of MetaPost picture expressions for use as an auxiliary input file.
\r
2464 Since MetaPost expects such files to have the extension .mpx, the output
\r
2465 is sometimes called an `mpx' file.
\r
2467 @d SHIFTS 100 /* maximum number of characters with special shifts */
\r
2468 @d MAXCHARS 256 /* character codes fall in the range 0..MAXCHARS-1 */
\r
2470 @d is_specchar(c) (!mpx->gflag && (c)<=2) /* does charcode c identify a special char? */
\r
2471 @d LWscale 0.03 /* line width for graphics as a fraction of pointsize */
\r
2472 @d YCORR 12.0 /* V coordinate of reference point in (big) points */
\r
2475 int next_specfnt[(max_fnums+1)]; /* used to link special fonts together */
\r
2476 int shiftchar[SHIFTS]; /* charcode of character to shift, else -1 */
\r
2477 float shifth[SHIFTS];
\r
2478 float shiftv[SHIFTS]; /* shift vals/fontsize (y is upward) */
\r
2479 int shiftptr; /* number of entries in shift tables */
\r
2480 int shiftbase[(max_fnums+1)]; /* initial index into shifth,shiftv,shiftchar */
\r
2481 int specfnt; /* int. num. of first special font (or FCOUNT) */
\r
2482 int *specf_tail ; /* tail of specfnt list |(*specf_tail==FCOUNT)| */
\r
2483 float cursize; /* current type size in (big) points */
\r
2484 unsigned int curfont; /* internal number for current font */
\r
2485 float Xslant; /* degrees additional slant for all fonts */
\r
2486 float Xheight; /* yscale fonts to this height if nonzero */
\r
2487 float sizescale; /* groff font size scaling factor */
\r
2488 int gflag; /* non-zero if using groff fonts */
\r
2489 float unit; /* (big) points per troff unit (0 when unset) */
\r
2491 @ @<Set initial...@>=
\r
2492 mpx->shiftptr = 0;
\r
2493 mpx->specfnt = (max_fnums+1);
\r
2494 mpx->specf_tail = &(mpx->specfnt);
\r
2496 mpx->lnno = 0; /* this is a reset */
\r
2498 mpx->h = 0; mpx->v = 0;
\r
2501 typedef char *(*mpx_file_finder)(MPX, const char *, const char *, int);
\r
2502 enum mpx_filetype {
\r
2503 mpx_tfm_format, /* kpse_tfm_format */
\r
2504 mpx_vf_format, /* kpse_vf_format */
\r
2505 mpx_trfontmap_format, /* kpse_mpsupport_format */
\r
2506 mpx_trcharadj_format, /* kpse_mpsupport_format */
\r
2507 mpx_desc_format, /* kpse_troff_font_format */
\r
2508 mpx_fontdesc_format, /* kpse_troff_font_format */
\r
2509 mpx_specchar_format /* kpse_mpsupport_format */
\r
2513 mpx_file_finder find_file;
\r
2515 @ @<Declarations@>=
\r
2516 static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype);
\r
2519 static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
\r
2521 if (mode[0] != 'r' || (! access (nam,R_OK)) || ftype) {
\r
2522 return strdup(nam);
\r
2527 @ @<Set initial...@>=
\r
2528 mpx->find_file = mpx_find_file;
\r
2530 @ @<Declarations@>=
\r
2531 static FILE *mpx_fsearch(MPX mpx, char *nam, int format);
\r
2534 static FILE *mpx_fsearch(MPX mpx, char *nam, int format) {
\r
2536 char *fname = (mpx->find_file)(mpx, nam, "r", format);
\r
2538 f = fopen(fname, "rb");
\r
2539 mpx_report(mpx,"%p = fopen(%s,\"rb\")",f, fname);
\r
2544 @ Hash tables (or rather: AVL lists)
\r
2553 static int mpx_comp_name (const void *pa, const void *pb, void *p) {
\r
2555 return strcmp (((const avl_entry *) pa)->name,
\r
2556 ((const avl_entry *) pb)->name);
\r
2560 static struct avl_table *mpx_avl_create (MPX mpx) {
\r
2561 struct avl_table *t;
\r
2562 t = avl_create(mpx_comp_name, NULL, NULL);
\r
2564 mpx_abort(mpx, "Memory allocation failure");
\r
2569 @ The only two operations on AVL lists are finding already existing
\r
2570 items, or interning new items. Finding is handled by explicit |avl_find|
\r
2571 calls where needed, but it is wise to have a wrapper around |avl_probe|
\r
2572 to check for memory errors.
\r
2575 static avl_entry * mpx_avl_probe(MPX mpx, struct avl_table *tab, avl_entry *p) {
\r
2576 avl_entry *r = (avl_entry *)avl_probe(tab,p);
\r
2578 mpx_abort(mpx,"Memory allocation failure");
\r
2584 @ Scanning Numbers
\r
2586 The standard functions atoi(), atof(), and sscanf() provide ways of reading
\r
2587 numbers from strings but they give no indication of how much of the string
\r
2588 is consumed. These homemade versions don't parse scientific notation.
\r
2591 char *arg_tail; /* char after the number just gotten; NULL on failure */
\r
2594 static int mpx_get_int(MPX mpx, char *s) {
\r
2595 register int i, d, neg;
\r
2598 for (neg = 0;; s++) {
\r
2601 else if (*s != ' ' && *s != '\t')
\r
2604 if (i = *s - '0', 0 > i || i > 9)
\r
2606 while (d = *++s - '0', 0 <= d && d <= 9)
\r
2608 mpx->arg_tail = s;
\r
2609 return neg ? -i : i;
\r
2611 mpx->arg_tail = NULL;
\r
2615 @ GROFF font description files use octal character codes
\r
2616 |groff_font(5)|: The code can be any integer. If it starts with
\r
2617 a 0 it will be interpreted as octal; if it starts with 0x
\r
2618 or 0X it will be intepreted as hexadecimal.
\r
2621 static int mpx_get_int_map(MPX mpx, char *s) {
\r
2625 i = strtol(s, &(mpx->arg_tail), 0);
\r
2626 if (s == mpx->arg_tail)
\r
2630 mpx->arg_tail = NULL;
\r
2634 @ Troff output files contain few if any non-integers, but this program is
\r
2635 prepared to read floats whenever they seem reasonable; i.e., when the
\r
2636 number is not being used for character positioning. (For non-PostScript
\r
2637 applications h and v are usually in pixels and should be integers.)
\r
2640 static float mpx_get_float(MPX mpx, char *s) {
\r
2641 register int d, neg, digits;
\r
2642 register float x, y;
\r
2646 for (neg = 0;; s++) {
\r
2649 else if (*s != ' ' && *s != '\t')
\r
2653 while (d = *s - '0', 0 <= d && d <= 9) {
\r
2660 while (d = *++s - '0', 0 <= d && d <= 9) {
\r
2667 if (digits == 0) {
\r
2668 mpx->arg_tail = NULL;
\r
2671 mpx->arg_tail = s;
\r
2672 return neg ? -x : x;
\r
2675 @ GROFF font description files have metrics field
\r
2676 of comma-separated integers. Traditional troff
\r
2677 have a float in this position. The value is not
\r
2678 used anyway - thus just skip the value,
\r
2679 eat all non-space chars.
\r
2682 static float mpx_get_float_map(MPX mpx, char *s) {
\r
2684 while (isspace(*s))
\r
2686 while (!isspace(*s) && *s)
\r
2689 mpx->arg_tail = s;
\r
2694 @ Reading Initialization Files
\r
2696 Read the database file, reserve internal font numbers and set
\r
2697 the |font_name| entries. Each line in the database file contains
\r
2698 |<troff-name>\t,PostScript-name>\t<TeX-name>|
\r
2700 |<troff-name>\t,PostScript-name>|
\r
2701 if the \TeX\ name matches the PostScript name. (|\t| means one or more tabs.)
\r
2704 struct avl_table *trfonts;
\r
2707 static void mpx_read_fmap(MPX mpx, char *dbase) {
\r
2710 char *nam; /* a font name being read */
\r
2713 fin = mpx_fsearch(mpx,dbase, mpx_trfontmap_format);
\r
2715 mpx_abort(mpx,"Cannot find %s", dbase);
\r
2717 mpx->trfonts = mpx_avl_create (mpx);
\r
2718 while ((buf = mpx_getline(mpx,fin)) != NULL) {
\r
2719 if (mpx->nfonts == (max_fnums+1))
\r
2720 mpx_abort(mpx,"Need to increase max_fnums");
\r
2722 while (*buf && *buf != '\t')
\r
2724 tmp = xmalloc(sizeof(avl_entry),1);
\r
2726 tmp->num = mpx->nfonts++;
\r
2727 (void)mpx_avl_probe (mpx,mpx->trfonts, tmp) ;
\r
2730 do buf++; while (*buf == '\t');
\r
2731 while (*buf && *buf != '\t'); buf++; /* skip over psname */
\r
2732 do buf++; while (*buf == '\t');
\r
2735 while (*buf); buf++;
\r
2737 mpx->font_name[tmp->num] = xstrdup(nam);
\r
2738 mpx->font_num[tmp->num] = -1; /* indicate font is not mounted */
\r
2741 mpx_fclose(mpx,fin);
\r
2745 @ Some characters need their coordinates shifted in order to agree with
\r
2746 troff's view of the world. Logically, this information belongs in the
\r
2747 font description files but it actually resides in a PostScript prolog
\r
2748 that the troff output processor dpost reads. Since that file is in
\r
2749 PostScript and subject to change, we read the same information from
\r
2750 a small auxiliary file that gives shift amounts relative to the font
\r
2751 size with y upward.
\r
2754 The PostScript prologue in GNU groff's font directory does not
\r
2755 contain any character shift information, so the following function
\r
2756 becomes redundant. Simply keeping an empty "trchars.adj" file
\r
2757 around will do fine without requiring any changes to this program.
\r
2760 static void mpx_read_char_adj(MPX mpx, char *adjfile) {
\r
2763 avl_entry tmp, *p;
\r
2766 fin = mpx_fsearch(mpx,adjfile, mpx_trcharadj_format);
\r
2768 mpx_abort(mpx,"Cannot find %s", adjfile);
\r
2770 for (i = 0; i < mpx->nfonts; i++)
\r
2771 mpx->shiftbase[i] = 0;
\r
2772 while (fgets(buf, 200, fin) != NULL) {
\r
2773 if (mpx->shiftptr == SHIFTS - 1)
\r
2774 mpx_abort(mpx,"Need to increase SHIFTS");
\r
2775 if (buf[0] != ' ' && buf[0] != '\t') {
\r
2776 for (i = 0; buf[i] != '\0'; i++)
\r
2777 if (buf[i] == '\n')
\r
2779 mpx->shiftchar[mpx->shiftptr++] = -1;
\r
2781 p = (avl_entry *)avl_find (mpx->trfonts, &tmp);
\r
2783 mpx_abort(mpx,"%s refers to unknown font %s", adjfile, buf);
\r
2784 mpx->shiftbase[p->num] = mpx->shiftptr;
\r
2787 mpx->shiftchar[mpx->shiftptr] = mpx_get_int(mpx,buf);
\r
2788 mpx->shifth[mpx->shiftptr] = mpx_get_float(mpx,mpx->arg_tail);
\r
2789 mpx->shiftv[mpx->shiftptr] = -mpx_get_float(mpx,mpx->arg_tail);
\r
2790 if (mpx->arg_tail == NULL)
\r
2791 mpx_abort(mpx,"Bad shift entry : \"%s\"", buf);
\r
2795 mpx->shiftchar[mpx->shiftptr++] = -1;
\r
2796 mpx_fclose(mpx,fin);
\r
2799 @ Read the DESC file of the troff device to gather information
\r
2800 about sizescale and whether running under groff.
\r
2802 Ignore all commands not specially handled. This relieves
\r
2803 of collecting commands without arguments here and also
\r
2804 makes the program more robust in case of future DESC
\r
2808 static void mpx_read_desc(MPX mpx) {
\r
2809 const char *const k1[] = {
\r
2810 "res", "hor", "vert", "unitwidth", "paperwidth",
\r
2811 "paperlength", "biggestfont", "spare2", "encoding",
\r
2814 const char *const g1[] = {
\r
2815 "family", "paperheight", "postpro", "prepro",
\r
2816 "print", "image_generator", "broken",
\r
2823 fp = mpx_fsearch(mpx,"DESC", mpx_desc_format);
\r
2825 mpx_abort(mpx,"Cannot find DESC");
\r
2826 while (fscanf(fp, "%199s", cmd) != EOF) {
\r
2827 if (*cmd == '#') {
\r
2828 while ((i = getc(fp)) != EOF && i != '\n');
\r
2831 if (strcmp(cmd, "fonts") == 0) {
\r
2832 if (fscanf(fp, "%d", &n) != 1)
\r
2834 for (i = 0; i < n; i++)
\r
2835 if (fscanf(fp, "%*s") == EOF)
\r
2837 } else if (strcmp(cmd, "sizes") == 0) {
\r
2838 while (fscanf(fp, "%d", &n) == 1 && n != 0);
\r
2839 } else if (strcmp(cmd, "styles") == 0 ||
\r
2840 strcmp(cmd, "papersize") == 0) {
\r
2842 while ((i = getc(fp)) != EOF && i != '\n');
\r
2843 } else if (strcmp(cmd, "sizescale") == 0) {
\r
2844 if (fscanf(fp, "%d", &n) == 1)
\r
2845 mpx->sizescale = n;
\r
2847 } else if (strcmp(cmd, "charset") == 0) {
\r
2850 for (i = 0; k1[i]; i++)
\r
2851 if (strcmp(cmd, k1[i]) == 0) {
\r
2852 if (fscanf(fp, "%*s") == EOF)
\r
2857 for (i = 0; g1[i]; i++)
\r
2858 if (strcmp(cmd, g1[i]) == 0) {
\r
2859 if (fscanf(fp, "%*s") == EOF)
\r
2869 @ Given one line from the character description file for the font with
\r
2870 internal number f, save the appropriate data in the charcodes[f] table.
\r
2871 A return value of zero indicates a syntax error.
\r
2874 GNU groff uses an extended font description file format documented
\r
2875 in |groff_font(5)|. In order to allow parsing of groff's font files,
\r
2876 this function needs to be rewritten as follows:
\r
2878 \item{1.}The `metrics' field parsed by |mpx_get_float(lin);| may include
\r
2879 a comma-separated list of up to six decimal integers rather
\r
2880 than just a single floating-point number.
\r
2882 \item{2.}The `charcode' field parsed by |lastcode = mpx_get_int(arg_tail);|
\r
2883 may be given either in decimal, octal, or hexadecimal format.
\r
2886 struct avl_table *charcodes[(max_fnums+1)]; /* hash tables for translating char names */
\r
2889 static int mpx_scan_desc_line(MPX mpx, int f, char *lin) {
\r
2890 static int lastcode;
\r
2894 while (*lin != ' ' && *lin != '\t' && *lin != '\0')
\r
2896 s = xmalloc(lin-t+1,1);
\r
2897 strncpy(s,t,lin-t);
\r
2898 while (*lin == ' ' || *lin == '\t')
\r
2900 if (*lin == '"') {
\r
2901 if (lastcode < MAXCHARS) {
\r
2902 tmp = xmalloc(sizeof(avl_entry),1);
\r
2904 tmp->num = lastcode;
\r
2905 (void)mpx_avl_probe (mpx, mpx->charcodes[f],tmp);
\r
2908 (void) mpx_get_float_map(mpx,lin);
\r
2909 (void) mpx_get_int(mpx,mpx->arg_tail);
\r
2910 lastcode = mpx_get_int_map(mpx,mpx->arg_tail);
\r
2911 if (mpx->arg_tail == NULL)
\r
2913 if (lastcode < MAXCHARS) {
\r
2914 tmp = xmalloc(sizeof(avl_entry),1);
\r
2916 tmp->num = lastcode;
\r
2917 (void)mpx_avl_probe (mpx, mpx->charcodes[f],tmp);
\r
2923 @ Read the font description file for the font with the given troff name
\r
2924 and update the data structures. The result is the internal font number.
\r
2927 static int mpx_read_fontdesc(MPX mpx, char *nam) { /* troff name */
\r
2929 avl_entry tmp, *p;
\r
2930 FILE *fin; /* input file */
\r
2931 int f; /* internal font number */
\r
2933 if (mpx->unit == 0.0)
\r
2934 mpx_abort(mpx, "Resolution is not set soon enough");
\r
2936 p = (avl_entry *)avl_find (mpx->trfonts, &tmp);
\r
2938 mpx_abort(mpx, "Font was not in map file");
\r
2940 fin = mpx_fsearch(mpx, nam, mpx_fontdesc_format);
\r
2942 mpx_abort(mpx,"Cannot find %s", nam);
\r
2944 if (fgets(buf, 200, fin) == NULL)
\r
2945 mpx_abort(mpx, "Description file for %s ends unexpectedly", nam);
\r
2946 if (strncmp(buf, "special", 7) == 0) {
\r
2947 *(mpx->specf_tail) = f;
\r
2948 mpx->next_specfnt[f] = (max_fnums+1);
\r
2949 mpx->specf_tail = &(mpx->next_specfnt[f]);
\r
2950 } else if (strncmp(buf, "charset", 7) == 0)
\r
2953 mpx->charcodes[f] = mpx_avl_create (mpx);
\r
2954 while (fgets(buf, 200, fin) != NULL)
\r
2955 if (mpx_scan_desc_line(mpx, f, buf) == 0)
\r
2956 mpx_abort(mpx, "%s has a bad line in its description file: %s", nam, buf);
\r
2957 mpx_fclose(mpx,fin);
\r
2961 @ Page and Character Output
\r
2964 boolean graphics_used; /* nonzero if any graphics seen on this page */
\r
2966 float dmp_str_v; /* corrected start pos for current out string */
\r
2967 float dmp_str_h2; /* where the current output string ends */
\r
2968 float str_size; /* point size for this text string */
\r
2971 @ Print any transformations required by the current Xslant and Xheight settings.
\r
2974 static void mpx_slant_and_ht(MPX mpx);
\r
2977 static void mpx_slant_and_ht(MPX mpx) {
\r
2979 if (mpx->Xslant != 0.0) {
\r
2980 fprintf(mpx->mpxfile, " slanted%.5f", mpx->Xslant);
\r
2983 if (mpx->Xheight != mpx->cursize && mpx->Xheight != 0.0 && mpx->cursize != 0.0) {
\r
2984 fprintf(mpx->mpxfile, " yscaled%.4f", mpx->Xheight / mpx->cursize);
\r
2988 fprintf(mpx->mpxfile, "\n ");
\r
2992 @ Output character number c in the font with internal number f.
\r
2995 static void mpx_set_num_char(MPX mpx, int f, int c) {
\r
2996 float hh, vv; /* corrected versions of h, v */
\r
3001 for (i = mpx->shiftbase[f]; mpx->shiftchar[i] >= 0; i++)
\r
3002 if (mpx->shiftchar[i] == c) {
\r
3003 hh += (mpx->cursize / mpx->unit) * mpx->shifth[i];
\r
3004 vv += (mpx->cursize / mpx->unit) * mpx->shiftv[i];
\r
3007 if (hh - mpx->dmp_str_h2 >= 1.0 || mpx->dmp_str_h2 - hh >= 1.0 ||
\r
3008 vv - mpx->dmp_str_v >= 1.0 || mpx->dmp_str_v - vv >= 1.0 ||
\r
3009 f != mpx->str_f || mpx->cursize != mpx->str_size) {
\r
3010 if (mpx->str_f >= 0)
\r
3011 mpx_finish_last_char(mpx);
\r
3012 else if (!mpx->fonts_used)
\r
3013 mpx_prepare_font_use(mpx); /* first font usage on this page */
\r
3014 if (!mpx->font_used[f])
\r
3015 mpx_first_use(mpx,f); /* first use of font f on this page */
\r
3016 fprintf(mpx->mpxfile, "_s((");
\r
3017 mpx->print_col = 3;
\r
3019 mpx->dmp_str_v = vv;
\r
3020 mpx->dmp_str_h1 = hh;
\r
3021 mpx->str_size = mpx->cursize;
\r
3023 mpx_print_char(mpx,c);
\r
3024 mpx->dmp_str_h2 = hh + char_width(f,c);
\r
3027 @ Output a string.
\r
3030 static void mpx_set_string(MPX mpx, char *cname) {
\r
3031 float hh; /* corrected version of h, current horisontal position */
\r
3036 mpx_set_num_char(mpx,mpx->curfont, *cname);
\r
3037 hh += char_width(mpx->curfont,(int)*cname);
\r
3038 while (*++cname) {
\r
3039 mpx_print_char(mpx,*cname);
\r
3040 hh += char_width(mpx->curfont,(int)*cname);
\r
3042 mpx->h = (double)floor(hh+0.5);
\r
3043 mpx_finish_last_char(mpx);
\r
3046 @ Special Characters
\r
3048 Given the troff name of a special character, this routine finds its
\r
3049 definition and copies it to the MPX file. It also finds the name of
\r
3050 the vardef macro and returns that name. The name should be C.<something>.
\r
3053 TH: A bit of trickery is added here for case-insensitive
\r
3054 file systems. This aliasing allows the CHARLIB directory
\r
3055 to exist on DVDs, for example.
\r
3056 It is a hack, I know. I've stuck to names on TeXLive.
\r
3058 @d test_redo_search do {
\r
3060 deff = mpx_fsearch(mpx, cname, mpx_specchar_format);
\r
3064 static char *mpx_copy_spec_char(MPX mpx, char *cname) {
\r
3068 char specintro[] = "vardef "; /* MetaPost name follows this */
\r
3069 unsigned k = 0; /* how much of specintro so far */
\r
3070 if (strcmp(cname, "ao") == 0) {
\r
3071 deff = mpx_fsearch(mpx, "ao.x", mpx_specchar_format);
\r
3073 } else if (strcmp(cname, "lh") == 0) {
\r
3074 deff = mpx_fsearch(mpx, "lh.x", mpx_specchar_format);
\r
3076 } else if (strcmp(cname, "~=") == 0) {
\r
3077 deff = mpx_fsearch(mpx, "twiddle", mpx_specchar_format);
\r
3080 deff = mpx_fsearch(mpx, cname, mpx_specchar_format);
\r
3083 mpx_abort(mpx, "No vardef in charlib/%s", cname);
\r
3085 while (k < strlen(specintro)) {
\r
3086 if ((c = getc(deff)) == EOF)
\r
3087 mpx_abort(mpx, "No vardef in charlib/%s", cname);
\r
3088 putc(c, mpx->mpxfile);
\r
3089 if (c == specintro[k])
\r
3094 s = xmalloc(mpx->bufsize,1);
\r
3096 while ((c = getc(deff)) != '(') {
\r
3098 mpx_abort(mpx, "vardef in charlib/%s has no arguments", cname);
\r
3099 putc(c, mpx->mpxfile);
\r
3102 putc(c, mpx->mpxfile);
\r
3104 while ((c = getc(deff)) != EOF);
\r
3105 putc(c, mpx->mpxfile);
\r
3110 @ When given a character name instead of a number, we need to check if
\r
3111 it is a special character and download the definition if necessary.
\r
3112 If the character is not in the current font we have to search the special
\r
3116 struct avl_table *spec_tab;
\r
3118 @ The |spec_tab| avl table combines character names with macro names.
\r
3127 static void mpx_set_char(MPX mpx, char *cname) {
\r
3129 avl_entry tmp, *p;
\r
3132 if (*cname == ' ' || *cname == '\t')
\r
3136 p = avl_find(mpx->charcodes[f], &tmp);
\r
3138 for (f = mpx->specfnt; f != (max_fnums+1); f = mpx->next_specfnt[f]) {
\r
3139 p = avl_find(mpx->charcodes[f], &tmp);
\r
3143 mpx_abort(mpx, "There is no character %s", cname);
\r
3147 if (!is_specchar(c)) {
\r
3148 mpx_set_num_char(mpx, f, c);
\r
3150 if (mpx->str_f >= 0)
\r
3151 mpx_finish_last_char(mpx);
\r
3152 if (!mpx->fonts_used)
\r
3153 mpx_prepare_font_use(mpx);
\r
3154 if (!mpx->font_used[f])
\r
3155 mpx_first_use(mpx, f);
\r
3156 if (mpx->spec_tab)
\r
3157 mpx->spec_tab = mpx_avl_create (mpx);
\r
3158 sp = xmalloc(sizeof(spec_entry),1);
\r
3161 sp = (spec_entry *)avl_probe(mpx->spec_tab,sp);
\r
3163 mpx_abort(mpx,"Memory allocation failure");
\r
3164 if (sp->mac == NULL) {
\r
3165 sp->mac = mpx_copy_spec_char(mpx, cname); /* this won't be NULL */
\r
3167 fprintf(mpx->mpxfile, "_s(%s(n%d)", sp->mac, mpx->font_num[f]);
\r
3168 mpx_slant_and_ht(mpx);
\r
3169 fprintf(mpx->mpxfile, ",%.5f,%.4f,%.4f);\n",
\r
3170 (mpx->cursize/mpx->font_design_size[f])*1.00375, mpx->h*mpx->unit, YCORR-mpx->v*mpx->unit);
\r
3174 @ Font Definitions
\r
3176 Mount the font with troff name nam at external font number n and read any
\r
3177 necessary font files.
\r
3180 static void mpx_do_font_def(MPX mpx, int n, char *nam) {
\r
3183 avl_entry tmp, *p;
\r
3185 p = (avl_entry *) avl_find (mpx->trfonts, &tmp);
\r
3187 mpx_abort(mpx, "Font %s was not in map file", nam);
\r
3189 if ( mpx->info_base[f]==max_widths ) {
\r
3190 mpx_read_fontdesc(mpx, nam);
\r
3191 mpx->cur_name = xstrdup(mpx->font_name[f]);
\r
3192 if (! mpx_open_tfm_file(mpx) )
\r
3193 font_abort("No TFM file found for ",f);
\r
3194 @.no TFM file found@>
\r
3195 mpx_in_TFM(mpx, f);
\r
3197 for (k = 0; k < mpx->nfonts; k++)
\r
3198 if (mpx->font_num[k] == n)
\r
3199 mpx->font_num[k] = -1;
\r
3200 mpx->font_num[f] = n;
\r
3201 @<Do any other initialization required for the new font |f|@>;
\r
3206 @ Time on `makepath pencircle'
\r
3208 Given the control points of a cubic Bernstein polynomial, evaluate it at t.
\r
3210 @d Speed ((float) (PI/4.0))
\r
3213 static float mpx_b_eval(const float *xx, float t) {
\r
3215 register int i, j;
\r
3216 for (i = 0; i <= 3; i++)
\r
3218 for (i = 3; i > 0; i--)
\r
3219 for (j = 0; j < i; j++)
\r
3220 zz[j] += t * (zz[j + 1] - zz[j]);
\r
3225 @ Find the direction angle at time t on the path `makepath pencircle'.
\r
3226 The tables below give the Bezier control points for MetaPost's cubic
\r
3227 approximation to the first octant of a unit circle.
\r
3230 static const float xx[] = { 1.0, 1.0, 0.8946431597, 0.7071067812 };
\r
3231 static const float yy[] = { 0.0, 0.2652164899, 0.5195704026, 0.7071067812 };
\r
3234 static float mpx_circangle(float t) {
\r
3238 return (float) atan(mpx_b_eval(yy, t) /
\r
3239 mpx_b_eval(xx, t)) + ti * Speed;
\r
3243 @ Find the spline parameter where `makepath pencircle' comes closest to
\r
3244 (cos(a)/2,sin(a)/2).
\r
3247 static float mpx_circtime(float a) {
\r
3251 for (i = 2; --i >= 0;)
\r
3252 t += (a - mpx_circangle(t)) / Speed;
\r
3262 float gy; /* current point for graphics (init. (h,YCORR/mpx->unit-v) */
\r
3265 static void mpx_prepare_graphics(MPX mpx) {
\r
3267 fprintf(mpx->mpxfile, "vardef _D(expr _d)expr _q =\n");
\r
3268 fprintf(mpx->mpxfile,
\r
3269 " addto _p doublepath _q withpen pencircle scaled _d; enddef;\n");
\r
3270 mpx->graphics_used = true;
\r
3274 @ This function prints the current position (gx,gy). Then if it can read dh dv
\r
3275 from string s, it increments (gx,gy) and prints "--". By returning the rest
\r
3276 of the string s or NULL if nothing could be read from s, it provides the
\r
3277 argument for the next iteration.
\r
3280 static char *mpx_do_line(MPX mpx, char *s) {
\r
3283 fprintf(mpx->mpxfile, "(%.3f,%.3f)", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
\r
3284 dh = mpx_get_float(mpx, s);
\r
3285 dv = mpx_get_float(mpx, mpx->arg_tail);
\r
3286 if (mpx->arg_tail == NULL)
\r
3290 fprintf(mpx->mpxfile, "--\n");
\r
3291 return mpx->arg_tail;
\r
3295 @ Function |spline_seg()| reads two pairs of (dh,dv) increments and prints the
\r
3296 corresponding quadratic B-spline segment, leaving the ending point to be
\r
3297 printed next time. The return value is the string with the first (dh,dv)
\r
3298 pair lopped off. If only one pair of increments is found, we prepare to
\r
3299 terminate the iteration by printing last time's ending point and returning
\r
3303 static char * mpx_spline_seg(MPX mpx, char *s) {
\r
3304 float dh1, dv1, dh2, dv2;
\r
3306 dh1 = mpx_get_float(mpx, s);
\r
3307 dv1 = mpx_get_float(mpx, mpx->arg_tail);
\r
3308 if (mpx->arg_tail == NULL)
\r
3309 mpx_abort(mpx, "Missing spline increments");
\r
3310 s = mpx->arg_tail;
\r
3311 fprintf(mpx->mpxfile, "(%.3f,%.3f)", (mpx->gx + .5 * dh1) * mpx->unit,
\r
3312 (mpx->gy - .5 * dv1) * mpx->unit);
\r
3315 dh2 = mpx_get_float(mpx, s);
\r
3316 dv2 = mpx_get_float(mpx, mpx->arg_tail);
\r
3317 if (mpx->arg_tail == NULL)
\r
3319 fprintf(mpx->mpxfile, "..\ncontrols (%.3f,%.3f) and (%.3f,%.3f)..\n",
\r
3320 (mpx->gx - dh1 / 6.0) * mpx->unit, (mpx->gy + dv1 / 6.0) * mpx->unit,
\r
3321 (mpx->gx + dh2 / 6.0) * mpx->unit, (mpx->gy - dv2 / 6.0) * mpx->unit);
\r
3326 @ Draw an ellipse with the given major and minor axes.
\r
3329 static void mpx_do_ellipse(MPX mpx, float a, float b) {
\r
3331 fprintf(mpx->mpxfile, "makepath(pencircle xscaled %.3f\n yscaled %.3f",
\r
3332 a * mpx->unit, b * mpx->unit);
\r
3333 fprintf(mpx->mpxfile, " shifted (%.3f,%.3f));\n", (mpx->gx + .5 * a) * mpx->unit,
\r
3334 mpx->gy * mpx->unit);
\r
3339 @ Draw a counter-clockwise arc centered at (cx,cy) with initial and final radii
\r
3340 (ax,ay) and (bx,by) respectively.
\r
3344 void mpx_do_arc(MPX mpx, float cx, float cy, float ax, float ay, float bx, float by) {
\r
3347 t1 = mpx_circtime(atan2(ay, ax));
\r
3348 t2 = mpx_circtime(atan2(by, bx));
\r
3351 fprintf(mpx->mpxfile, "subpath (%.5f,%.5f) of\n", t1, t2);
\r
3352 fprintf(mpx->mpxfile,
\r
3353 " makepath(pencircle scaled %.3f shifted (%.3f,%.3f));\n",
\r
3354 2.0 * sqrt(ax * ax + ay * ay) * mpx->unit, cx * mpx->unit, cy * mpx->unit);
\r
3355 mpx->gx = cx + bx;
\r
3356 mpx->gy = cy + by;
\r
3361 @ String s is everything following the initial `D' in a troff graphics command.
\r
3364 static void mpx_do_graphic(MPX mpx, char *s) {
\r
3365 float h1, v1, h2, v2;
\r
3367 mpx_finish_last_char(mpx);
\r
3368 /* GROFF uses Fd to set fill color for solid drawing objects to the
\r
3369 default, so just ignore that.
\r
3371 if (s[0] == 'F' && s[1] == 'd')
\r
3373 mpx->gx = (float) mpx->h;
\r
3374 mpx->gy = YCORR / mpx->unit - ((float) mpx->v);
\r
3375 if (!mpx->graphics_used)
\r
3376 mpx_prepare_graphics(mpx);
\r
3377 fprintf(mpx->mpxfile, "D(%.4f) ", LWscale * mpx->cursize);
\r
3380 h1 = mpx_get_float(mpx,s);
\r
3381 if (mpx->arg_tail == NULL)
\r
3382 mpx_abort(mpx,"Bad argument in %s", s-2);
\r
3383 mpx_do_ellipse(mpx,h1, h1);
\r
3386 h1 = mpx_get_float(mpx,s);
\r
3387 v1 = mpx_get_float(mpx,mpx->arg_tail);
\r
3388 if (mpx->arg_tail == NULL)
\r
3389 mpx_abort(mpx,"Bad argument in %s", s - 2);
\r
3390 mpx_do_ellipse(mpx,h1, v1);
\r
3393 fprintf(mpx->mpxfile, "reverse ");
\r
3394 /* fall through */
\r
3396 h1 = mpx_get_float(mpx,s);
\r
3397 v1 = mpx_get_float(mpx,mpx->arg_tail);
\r
3398 h2 = mpx_get_float(mpx,mpx->arg_tail);
\r
3399 v2 = mpx_get_float(mpx,mpx->arg_tail);
\r
3400 if (mpx->arg_tail == NULL)
\r
3401 mpx_abort(mpx,"Bad argument in %s", s - 2);
\r
3402 mpx_do_arc(mpx,mpx->gx + h1, mpx->gy - v1, -h1, v1, h2, -v2);
\r
3407 s = mpx_do_line(mpx,s);
\r
3408 fprintf(mpx->mpxfile, ";\n");
\r
3412 s = mpx_spline_seg(mpx,s);
\r
3413 while (s != NULL);
\r
3414 fprintf(mpx->mpxfile, ";\n");
\r
3417 fprintf(mpx->mpxfile, "(%.3f,%.3f)--", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
\r
3419 s = mpx_spline_seg(mpx,s);
\r
3420 while (s != NULL);
\r
3421 fprintf(mpx->mpxfile, "--(%.3f,%.3f);\n", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
\r
3424 mpx_abort(mpx,"Unknown drawing function %s", s - 2);
\r
3426 mpx->h = (int) floor(mpx->gx + .5);
\r
3427 mpx->v = (int) floor(YCORR / mpx->unit + .5 - mpx->gy);
\r
3432 @ Interpreting Troff Output
\r
3435 static void mpx_change_font(MPX mpx, int f) {
\r
3436 for (mpx->curfont = 0; mpx->curfont < mpx->nfonts; mpx->curfont++)
\r
3437 if (mpx->font_num[mpx->curfont] == f)
\r
3439 mpx_abort(mpx,"Bad font setting");
\r
3443 @ String s0 is everything following the initial `x' in a troff device control
\r
3444 command. A zero result indicates a stop command.
\r
3447 static int mpx_do_x_cmd(MPX mpx, char *s0)
\r
3454 while (*s == ' ' || *s == '\t')
\r
3458 if (mpx->unit != 0.0)
\r
3459 mpx_abort(mpx,"Attempt to reset resolution");
\r
3460 while (*s != ' ' && *s != '\t')
\r
3462 mpx->unit = mpx_get_float(mpx,s);
\r
3463 if (mpx->unit <= 0.0)
\r
3464 mpx_abort(mpx,"Bad resolution: x %s", s0);
\r
3465 mpx->unit = 72.0 / mpx->unit;
\r
3468 while (*s != ' ' && *s != '\t')
\r
3470 n = mpx_get_int(mpx,s);
\r
3471 if (mpx->arg_tail == NULL)
\r
3472 mpx_abort(mpx,"Bad font def: x %s", s0);
\r
3473 s = mpx->arg_tail;
\r
3474 while (*s == ' ' || *s == '\t')
\r
3476 mpx_do_font_def(mpx,n, s);
\r
3481 while (*s != ' ' && *s != '\t')
\r
3483 mpx->Xheight = mpx_get_float(mpx,s);
\r
3484 /* GROFF troff output is scaled |groff_out(5)|:
\r
3485 The argument to the s command is in scaled
\r
3486 points (units of points/n, where n is the argument
\r
3487 to the sizescale command in the DESC file.) The
\r
3488 argument to the x Height command is also in scaled points.
\r
3489 sizescale for groff devps is 1000
\r
3491 if (mpx->sizescale) {
\r
3492 if (mpx->unit != 0.0)
\r
3493 mpx->Xheight *= mpx->unit; /* ??? */
\r
3495 mpx->Xheight /= mpx->sizescale;
\r
3497 if (mpx->Xheight == mpx->cursize)
\r
3498 mpx->Xheight = 0.0;
\r
3501 while (*s != ' ' && *s != '\t')
\r
3503 mpx->Xslant = mpx_get_float(mpx,s) * (PI / 180.0);
\r
3504 x = cos(mpx->Xslant);
\r
3505 if (-1e-4 < x && x < 1e-4)
\r
3506 mpx_abort(mpx,"Excessive slant");
\r
3507 mpx->Xslant = sin(mpx->Xslant) / x;
\r
3510 /* do nothing */ ;
\r
3516 @ This routine reads commands from the troff output file up to and including
\r
3517 the next `p' or `x s' command. It also calls |set_num_char()| and |set_char()|
\r
3518 to generate output when appropriate. A zero result indicates that there
\r
3519 are no more pages to do.
\r
3522 GNU groff uses an extended device-independent output file format
\r
3523 documented in |groff_out(5)|. In order to allow parsing of groff's
\r
3524 output files, this function either needs to be extended to support
\r
3525 the new command codes, or else the use of the "t" and "u" commands
\r
3526 must be disabled by removing the line "tcommand" from the DESC file
\r
3527 in the \$(prefix)/lib/groff/devps directory.
\r
3530 static int mpx_do_page (MPX mpx, FILE *trf) {
\r
3534 mpx->h = mpx->v = 0;
\r
3535 while ((buf = mpx_getline(mpx, trf)) != NULL) {
\r
3538 while (*c != '\0') {
\r
3546 mpx->cursize = mpx_get_float(mpx,c + 1);
\r
3547 /* GROFF troff output is scaled
\r
3548 |groff_out(5)|: The argument to the s command is in scaled
\r
3549 points (units of points/n, where n is the argument
\r
3550 to the sizescale command in the DESC file.) The
\r
3551 argument to the x Height command is also in scaled
\r
3553 sizescale for groff devps is 1000
\r
3555 if (mpx->sizescale) {
\r
3556 if (mpx->unit != 0.0)
\r
3557 mpx->cursize *= mpx->unit; /* ??? */
\r
3559 mpx->cursize /= mpx->sizescale;
\r
3563 mpx_change_font(mpx, mpx_get_int(mpx,c + 1));
\r
3567 mpx_abort(mpx, "Bad c command in troff output");
\r
3574 while (*cc != ' ' && *cc != '\t' && *cc != '\0');
\r
3577 mpx_set_num_char(mpx, mpx->curfont, mpx_get_int(mpx,c + 1));
\r
3580 mpx->h = mpx_get_int(mpx, c + 1);
\r
3583 mpx->v = mpx_get_int(mpx, c + 1);
\r
3586 mpx->h += mpx_get_int(mpx, c + 1);
\r
3589 mpx->v += mpx_get_int(mpx, c + 1);
\r
3601 if (c[1] < '0' || c[1] > '9' || c[2] == '\0')
\r
3602 mpx_abort(mpx, "Bad nnc command in troff output");
\r
3603 mpx->h += 10 * (c[0] - '0') + c[1] - '0';
\r
3610 (void) mpx_get_int(mpx, c + 1);
\r
3611 (void) mpx_get_int(mpx, mpx->arg_tail);
\r
3614 mpx_do_graphic(mpx, c + 1);
\r
3617 if (!mpx_do_x_cmd(mpx, c + 1))
\r
3623 /* GROFF uses this command to report filename */
\r
3626 /* GROFF uses this command to control color */
\r
3629 /* GROFF uses this command to output a word with additional
\r
3630 white space between characters, not implemented
\r
3632 mpx_abort(mpx, "Bad command in troff output\n"
\r
3633 "change the DESC file for your GROFF PostScript device, remove tcommand");
\r
3635 /* GROFF uses this command to output a word */
\r
3639 while (*cc != ' ' && *cc != '\t' && *cc != '\0');
\r
3642 mpx_set_string(mpx, ++c);
\r
3647 mpx_abort(mpx, "Bad command in troff output");
\r
3653 mpx_set_char(mpx, ++c);
\r
3658 c = mpx->arg_tail;
\r
3660 eoln: /* do nothing */ ;
\r
3666 @ Main Dmp Program
\r
3668 @d dbname "trfonts.map" /* file for table of troff \& TFM font names */
\r
3669 @d adjname "trchars.adj" /* file for character shift amounts */
\r
3672 static int mpx_dmp(MPX mpx, char *infile) {
\r
3674 FILE *trf = mpx_xfopen(mpx,infile, "r");
\r
3675 mpx_open_mpxfile(mpx);
\r
3676 fprintf(mpx->mpxfile, mpx->banner);
\r
3677 fprintf (mpx->mpxfile,"\n");
\r
3678 mpx_read_desc(mpx);
\r
3679 mpx_read_fmap(mpx,dbname);
\r
3681 mpx_read_char_adj(mpx,adjname);
\r
3682 if (mpx_do_page(mpx, trf)) {
\r
3684 @<Do initialization required before starting a new page@>;
\r
3685 mpx_start_picture(mpx);
\r
3686 more = mpx_do_page(mpx,trf);
\r
3687 mpx_stop_picture(mpx);
\r
3688 fprintf(mpx->mpxfile, "mpxbreak\n");
\r
3691 mpx_fclose(mpx,trf);
\r
3692 mpx_fclose(mpx,mpx->mpxfile);
\r
3693 if ( mpx->history<=mpx_cksum_trouble )
\r
3696 return mpx->history;
\r
3703 Make an MPX file from the labels in a MetaPost source file,
\r
3704 using mpto and either dvitomp (TeX) or dmp (troff).
\r
3706 Started from a shell script initially based on John Hobby's original
\r
3707 version, that was then translated to C by Akira Kakuto (Aug 1997,
\r
3708 Aug 2001), and updated and largely rewritten by Taco Hoekwater (Nov 2006).
\r
3711 Differences between the script and this C version:
\r
3713 The script trapped HUP, INT, QUIT and TERM for cleaning up
\r
3714 temporary files. This is a refinement, and not portable.
\r
3716 The script put its own directory in front of the
\r
3717 executable search PATH. This is not portable either, and
\r
3718 it seems a safe bet that normal users do not have 'mpto',
\r
3719 'dvitomp', or 'dmp' commands in their path.
\r
3721 The command-line '-troff' now also accepts an optional argument.
\r
3723 The troff infile for error diagnostics is renamed "mpxerr.i",
\r
3724 not plain "mpxerr".
\r
3726 The original script deleted mpx*.* in the cleanup process.
\r
3728 That is a bit harder in C, because it requires reading the contents
\r
3729 of the current directory. The current program assumes that
\r
3730 opendir(), readdir() and closedir() are known everywhere where
\r
3731 the function getcwd() exists (except on WIN32, where it uses
\r
3732 |_findfirst| \& co).
\r
3734 If this assumption is false, you can define |NO_GETCWD|, and makempx
\r
3735 will revert to trying to delete only a few known extensions
\r
3737 There is a -debug switch, preventing the removal of tmp files
\r
3739 @d TMPNAME_EXT(a,b) { strcpy(a,tmpname); strcat(a,b); }
\r
3743 #define TEXERR "mpxerr.tex"
\r
3744 #define DVIERR "mpxerr.dvi"
\r
3745 #define TROFF_INERR "mpxerr.i"
\r
3746 #define TROFF_OUTERR "mpxerr.t"
\r
3749 static void mpx_rename (MPX mpx, char *a, char *b) {
\r
3750 mpx_report(mpx,"renaming %s to %s",a,b);
\r
3761 static void mpx_default_erasetmp(MPX mpx) {
\r
3764 if (mpx->mode==mpx_tex_mode) {
\r
3765 wrk = xstrdup(mpx->tex);
\r
3766 p = strrchr(wrk, '.');
\r
3767 *p = '\0'; strcat(wrk, ".aux"); remove(wrk);
\r
3768 *p = '\0'; strcat(wrk, ".pdf"); remove(wrk);
\r
3769 *p = '\0'; strcat(wrk, ".toc"); remove(wrk);
\r
3770 *p = '\0'; strcat(wrk, ".idx"); remove(wrk);
\r
3771 *p = '\0'; strcat(wrk, ".ent"); remove(wrk);
\r
3772 *p = '\0'; strcat(wrk, ".out"); remove(wrk);
\r
3773 *p = '\0'; strcat(wrk, ".nav"); remove(wrk);
\r
3774 *p = '\0'; strcat(wrk, ".snm"); remove(wrk);
\r
3775 *p = '\0'; strcat(wrk, ".tui"); remove(wrk);
\r
3780 @ @<Declarations@>=
\r
3781 static void mpx_erasetmp(MPX mpx);
\r
3784 static void mpx_cleandir(MPX mpx, char *cur_path) {
\r
3787 struct _finddata_t c_file;
\r
3790 struct dirent *entry;
\r
3793 wrk = xstrdup(mpx->tex);
\r
3794 p = strrchr(wrk, '.');
\r
3795 *p = '\0'; /* now wrk is identical to tmpname */
\r
3798 strcat(cur_path,"/*");
\r
3799 if ((hFile = _findfirst (cur_path, &c_file)) == -1L) {
\r
3800 mpx_default_erasetmp(mpx);
\r
3802 if (strstr(c_file.name,wrk)==c_file.name)
\r
3803 remove(c_file.name);
\r
3804 while (_findnext (hFile, &c_file) != -1L) {
\r
3805 if (strstr(c_file.name,wrk)==c_file.name)
\r
3806 remove(c_file.name);
\r
3808 _findclose (hFile); /* no more entries => close directory */
\r
3811 if ((d = opendir(cur_path)) == NULL) {
\r
3812 mpx_default_erasetmp(mpx);
\r
3814 while ((entry = readdir (d)) != NULL) {
\r
3815 if (strstr(entry->d_name,wrk)==entry->d_name)
\r
3816 remove(entry->d_name);
\r
3825 @ It is important that |mpx_erasetmp| remains silent.
\r
3826 If it find trouble, it should just ignore it.
\r
3828 The string |cur_path| is a little bit larger than needed, because that
\r
3829 allows the win32 code in |cleandir| to add the slash and asterisk for
\r
3830 globbing without having to reallocate the variable first.
\r
3834 #define GETCWD _getcwd
\r
3836 #define GETCWD getcwd
\r
3838 static void mpx_erasetmp(MPX mpx) {
\r
3839 char cur_path[1024];
\r
3842 if (mpx->tex[0] != '\0') {
\r
3844 if(GETCWD(cur_path,1020) == NULL) {
\r
3845 mpx_default_erasetmp(mpx); /* don't know where we are */
\r
3847 mpx_cleandir(mpx,cur_path);
\r
3853 @* Running the external typesetters.
\r
3855 First, here is a helper for messaging.
\r
3858 static char *mpx_print_command (MPX mpx, int cmdlength, char **cmdline) {
\r
3863 for (i = 0; i < cmdlength ; i++) {
\r
3864 l += strlen(cmdline[i])+1;
\r
3866 s = xmalloc(l,1); t=s;
\r
3867 for (i = 0; i < cmdlength ; i++) {
\r
3868 if (i>0) *t++ = ' ';
\r
3869 t = strcpy(t,cmdline[i]);
\r
3870 t += strlen(cmdline[i]);
\r
3875 @ This function unifies the external program calling across Posix-like and Win32
\r
3879 static int do_spawn (MPX mpx, char *cmd, char **options) {
\r
3885 mpx_abort(mpx, "fork failed: %s", strerror(errno));
\r
3887 if(execvp(cmd, options))
\r
3888 mpx_abort(mpx, "exec failed: %s", strerror(errno));
\r
3890 if (wait(&retcode)==child) {
\r
3891 retcode = (WIFEXITED(retcode) ? WEXITSTATUS(retcode) : -1);
\r
3893 mpx_abort(mpx, "wait failed: %s", strerror(errno));
\r
3899 return spawnvp(P_WAIT, cmd, (const char **)options);
\r
3905 #define nuldev "nul"
\r
3907 #define nuldev "/dev/null"
\r
3909 static int mpx_run_command(MPX mpx, char *inname, char *outname, int count, char **cmdl) {
\r
3912 int sav_o, sav_i; /* for I/O redirection */
\r
3913 FILE *fr, *fw; /* read and write streams for the command */
\r
3915 if (count < 1 || cmdl == NULL || cmdl[0] == NULL)
\r
3916 return -1; /* return non-zero by default, signalling an error */
\r
3918 s = mpx_print_command(mpx,count, cmdl);
\r
3919 mpx_report(mpx,"running command %s", s);
\r
3922 fr = mpx_xfopen(mpx,(inname ? inname : nuldev), "r");
\r
3923 fw = mpx_xfopen(mpx,(outname ? outname : nuldev), "wb");
\r
3924 @<Save and redirect the standard I/O@>;
\r
3925 retcode = do_spawn(mpx,cmdl[0], cmdl);
\r
3926 @<Restore the standard I/O@>;
\r
3927 mpx_fclose(mpx,fr);
\r
3928 mpx_fclose(mpx,fw);
\r
3932 @ @ Running Troff is more likely than not a series of pipes that
\r
3933 feed input to each other. Makempx does all of this itself by using
\r
3934 temporary files inbetween. That means we have to juggle about with
\r
3935 |stdin| and |stdout|.
\r
3937 This is the only non-ansi C bit of makempx.
\r
3938 @^system dependencies@>
\r
3940 @<Save and redirect the standard I/O@>=
\r
3943 #define DUPP _dup2
\r
3948 sav_i = DUP(fileno(stdin));
\r
3949 sav_o = DUP(fileno(stdout));
\r
3950 DUPP(fileno(fr), fileno(stdin));
\r
3951 DUPP(fileno(fw), fileno(stdout))
\r
3953 @ @<Restore the standard I/O@>=
\r
3954 DUPP(sav_i, fileno(stdin));
\r
3956 DUPP(sav_o, fileno(stdout));
\r
3959 @ The allocation of the array pointed to by |cmdline_addr| is of
\r
3960 course much larger than is really needed, but it will still only be a
\r
3961 few hunderd bytes at the most, and this ensures that the separate
\r
3962 parts of the |maincmd| will all fit.
\r
3964 @d split_command(a,b) mpx_do_split_command(mpx,a,&b,' ')
\r
3965 @d split_pipes(a,b) mpx_do_split_command(mpx,a,&b,'|')
\r
3969 mpx_do_split_command(MPX mpx, char *maincmd, char ***cmdline_addr, char target) {
\r
3975 int in_string = 0;
\r
3976 if (strlen(maincmd) == 0)
\r
3978 i = sizeof(char *)*(strlen(maincmd)+1);
\r
3979 cmdline = xmalloc(i,1);
\r
3980 memset(cmdline,0,i);
\r
3981 *cmdline_addr = cmdline;
\r
3984 while (maincmd[i] == ' ')
\r
3986 cmd = xstrdup(maincmd);
\r
3988 for (; i <= strlen(maincmd); i++) {
\r
3989 if (in_string == 1) {
\r
3990 if (cmd[i] == '"') {
\r
3993 } else if (in_string == 2) {
\r
3994 if (cmd[i] == '\'') {
\r
3998 if (cmd[i] == '"') {
\r
4000 } else if (cmd[i] == '\'') {
\r
4002 } else if (cmd[i] == target) {
\r
4004 cmdline[ret++] = piece;
\r
4005 while (i < strlen(maincmd) && cmd[(i + 1)] == ' ')
\r
4007 piece = cmd + i + 1;
\r
4012 cmdline[ret++] = piece;
\r
4018 char *maincmd; /* TeX command name */
\r
4021 static void mpx_command_cleanup (MPX mpx, char **cmdline) {
\r
4023 xfree(cmdline[0]);
\r
4030 static void mpx_command_error (MPX mpx, int cmdlength, char **cmdline) {
\r
4031 char *s = mpx_print_command(mpx, cmdlength, cmdline);
\r
4032 mpx_command_cleanup(mpx, cmdline);
\r
4033 mpx_abort(mpx, "Command failed: %s; see mpxerr.log", s);
\r
4039 typedef struct makempx_options {
\r
4047 mpx_file_finder find_file;
\r
4048 } makempx_options;
\r
4049 int mp_makempx (makempx_options *mpxopt) ;
\r
4054 @d ERRLOG "mpxerr.log"
\r
4055 @d MPXLOG "makempx.log"
\r
4058 int mp_makempx (makempx_options *mpxopt) {
\r
4060 char **cmdline, **cmdbits;
\r
4063 char tmpname[] = "mpXXXXXX";
\r
4064 int cmdlength = 1;
\r
4065 int cmdbitlength = 1;
\r
4066 if (!mpxopt->debug) {
\r
4067 @<Check if mp file is newer than mpxfile, exit if not@>;
\r
4069 mpx = malloc(sizeof(struct mpx_data));
\r
4071 return mpx_fatal_error;
\r
4072 mpx_initialize(mpx);
\r
4073 mpx->banner = mpxopt->banner;
\r
4074 mpx->mode = mpxopt->mode;
\r
4075 mpx->debug = mpxopt->debug;
\r
4076 if (mpxopt->find_file!=NULL)
\r
4077 mpx->find_file = mpxopt->find_file;
\r
4078 if (mpxopt->cmd!=NULL)
\r
4079 mpx->maincmd = xstrdup(mpxopt->cmd); /* valgrind says this leaks */
\r
4080 mpx->mpname = xstrdup(mpxopt->mpname);
\r
4081 mpx->mpxname = xstrdup(mpxopt->mpxname);
\r
4082 @<Install and test the non-local jump buffer@>;
\r
4085 mpx->errfile = stderr;
\r
4087 mpx->errfile = mpx_xfopen(mpx,MPXLOG, "wb");
\r
4089 mpx->progname = "makempx";
\r
4090 @<Initialize the |tmpname| variable@>;
\r
4091 if (mpxopt->mptexpre == NULL)
\r
4092 mpxopt->mptexpre = xstrdup("mptexpre.tex");
\r
4093 @<Run |mpto| on the mp file@>;
\r
4094 if (mpxopt->cmd==NULL)
\r
4096 if (mpx->mode == mpx_tex_mode) {
\r
4097 @<Run |TeX| and set up |infile| or abort@>;
\r
4098 if (mpx_dvitomp(mpx, infile)) {
\r
4099 mpx_rename(mpx, infile,DVIERR);
\r
4101 remove(mpx->mpxname);
\r
4102 mpx_abort(mpx, "Dvi conversion failed: %s %s\n",
\r
4103 DVIERR, mpx->mpxname);
\r
4105 } else if (mpx->mode == mpx_troff_mode) {
\r
4106 @<Run |Troff| and set up |infile| or abort@>;
\r
4107 if (mpx_dmp(mpx, infile)) {
\r
4108 mpx_rename(mpx,infile, TROFF_OUTERR);
\r
4109 mpx_rename(mpx,mpx->tex, TROFF_INERR);
\r
4111 remove(mpx->mpxname);
\r
4112 mpx_abort(mpx, "Troff conversion failed: %s %s\n",
\r
4113 TROFF_OUTERR, mpx->mpxname);
\r
4116 mpx_fclose(mpx,mpx->mpxfile);
\r
4118 mpx_fclose(mpx,mpx->errfile);
\r
4119 if (!mpx->debug) {
\r
4124 mpx_erasetmp(mpx);
\r
4126 retcode = mpx->history;
\r
4127 mpx_xfree(mpx->buf);
\r
4128 mpx_xfree(mpx->maincmd);
\r
4129 for (i = 0; i < (int)mpx->nfonts; i++)
\r
4130 mpx_xfree(mpx->font_name[i]);
\r
4132 if (retcode == mpx_cksum_trouble)
\r
4137 @ \TeX\ has to operate on an actual input file, so we have to append
\r
4138 that to the command line.
\r
4140 @<Run |TeX| and set ...@>=
\r
4143 mpx->maincmd = xrealloc(mpx->maincmd,strlen(mpx->maincmd)+strlen(mpx->tex)+2,1);
\r
4144 strcat(mpx->maincmd, " ");
\r
4145 strcat(mpx->maincmd, mpx->tex);
\r
4146 cmdlength = split_command(mpx->maincmd, cmdline);
\r
4148 retcode = mpx_run_command(mpx, NULL, NULL, cmdlength, cmdline);
\r
4150 TMPNAME_EXT(log, ".log");
\r
4152 TMPNAME_EXT(infile, ".dvi");
\r
4155 mpx_rename(mpx,mpx->tex, TEXERR);
\r
4156 mpx_rename(mpx,log, ERRLOG);
\r
4157 mpx_command_error(mpx, cmdlength, cmdline);
\r
4159 mpx_command_cleanup(mpx, cmdline);
\r
4162 @ @<Run |Troff| and set ...@>=
\r
4164 char *cur_in, *cur_out;
\r
4165 char tmp_a[15], tmp_b[15];
\r
4166 TMPNAME_EXT(tmp_a, ".t");
\r
4167 TMPNAME_EXT(tmp_b, ".tmp");
\r
4168 cur_in = mpx->tex;
\r
4171 /* split the command in bits */
\r
4172 cmdbitlength = split_pipes(mpx->maincmd, cmdbits);
\r
4174 for (i = 0; i < cmdbitlength; i++) {
\r
4175 if (cmdline!=NULL) free(cmdline);
\r
4176 cmdlength = split_command(cmdbits[i], cmdline);
\r
4177 retcode = mpx_run_command(mpx, cur_in, cur_out, cmdlength, cmdline);
\r
4180 mpx_rename(mpx,mpx->tex, TROFF_INERR);
\r
4181 mpx_command_error(mpx, cmdlength, cmdline);
\r
4183 if (i < cmdbitlength - 1) {
\r
4191 strcpy(infile,cur_in);
\r
4194 if (tmp_a!=infile) { remove(tmp_a); }
\r
4195 if (tmp_b!=infile) { remove(tmp_b); }
\r
4198 @ If MPX file is up-to-date or if MP file does not exist, do nothing.
\r
4200 @<Check if mp file is newer than mpxfile, exit if not@>=
\r
4201 if (mpx_newer(mpxopt->mpname, mpxopt->mpxname))
\r
4206 @<Initialize the |tmpname| variable@>=
\r
4207 #ifdef HAVE_MKSTEMP
\r
4208 i = mkstemp(tmpname);
\r
4210 sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
\r
4216 #ifdef HAVE_MKTEMP
\r
4218 char *tmpstring = mktemp(tmpname);
\r
4219 if ((tmpstring == NULL) || strlen(tmpname) == 0) {
\r
4220 sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
\r
4222 /* this should not really be needed, but better
\r
4223 safe than sorry. */
\r
4224 if (tmpstring != tmpname) {
\r
4225 i = strlen(tmpstring);
\r
4227 strncpy(tmpname, tmpstring, i);
\r
4232 sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
\r