2 % MetaPost command-line program, by Taco Hoekwater. Public domain.
\r
4 \def\title{Creating mpx files}
\r
5 \def\hang{\hangindent 3em\indent\ignorespaces}
\r
7 \def\LaTeX{{\rm L\kern-.36em\raise.3ex\hbox{\sc a}\kern-.15em
\r
8 T\kern-.1667em\lower.7ex\hbox{E}\kern-.125emX}}
\r
10 \def\(#1){} % this is used to make section names sort themselves better
\r
11 \def\9#1{} % this is used for sort keys in the index
\r
16 @* \[1] Makempx overview.
\r
18 This source file implements the makempx functionality for the new \MP.
\r
19 It includes all of the functional code from the old standalone programs
\r
26 combined into one, with many changes to make all of the code cooperate
\r
31 The local C preprocessor definitions have to come after the C includes
\r
32 in order to prevent name clashes.
\r
42 #include <errno.h> /* TODO autoconf ? */
\r
43 /* unistd.h is needed for every non-Win32 platform, and we assume
\r
44 * that implies that sys/types.h is also present
\r
47 #include <sys/types.h>
\r
53 #include <process.h>
\r
56 # include <sys/wait.h>
\r
59 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
\r
62 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
\r
70 # include <dirent.h>
\r
72 # define dirent direct
\r
73 # if HAVE_SYS_NDIR_H
\r
74 # include <sys/ndir.h>
\r
77 # include <sys/dir.h>
\r
85 #include <sys/stat.h>
\r
90 #define trunc(x) ((integer) (x))
\r
91 #define fabs(x) ((x)<0?(-(x)):(x))
\r
92 #define floor(x) ((integer) (fabs(x)))
\r
94 #define PI 3.14159265358979323846
\r
102 From the Pascal code of DVItoMP two implicit types are inherited: |boolean| and
\r
105 The more complex datatypes are defined in the following sections.
\r
111 typedef signed int integer;
\r
112 typedef signed int boolean;
\r
116 @ The single most important data structure is the structure
\r
117 |mpx_data|. It contains all of the global state for a specific
\r
118 |makempx| run. A pointer to it is passed as the first argument to just
\r
119 about every function call.
\r
121 One of the fields is a bit special because it is so important: |mode|
\r
122 is the decider between running \TeX\ or Troff as the typesetting
\r
130 typedef struct mpx_data * MPX;
\r
132 @ @<C Data Types@>=
\r
133 @<Types in the outer block@>
\r
134 typedef struct mpx_data {
\r
139 @ Here are some macros for common programming idioms.
\r
141 @d MAXINT 0x7FFFFF /* somewhat arbitrary */
\r
143 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
\r
144 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
\r
146 @ Once an MPX object is allocated, the memory it occupies needs to be
\r
147 initialized to a usable state. This procedure gets things started
\r
150 This function is not allowed to run |mpx_abort| because at this
\r
151 point the jump buffer is not yet initialized, so it should only
\r
152 be used for things that cannot go wrong!
\r
155 static void mpx_initialize (MPX mpx) {
\r
156 memset(mpx,0,sizeof(struct mpx_data));
\r
157 @<Set initial values@>@/
\r
160 @ A global variable |history| keeps track of what type of errors have
\r
161 occurred with the hope that that \MP\ can be warned of any problems.
\r
164 enum mpx_history_states {
\r
165 mpx_spotless=0, /* |history| value when no problems have been found */
\r
166 mpx_cksum_trouble, /* |history| value there have been font checksum mismatches */
\r
167 mpx_warning_given, /* |history| value after a recoverable error */
\r
168 mpx_fatal_error /* |history| value if processing had to be aborted */
\r
176 mpx->history=mpx_spotless;
\r
178 @ The structure has room for the names and the |FILE *| for the
\r
179 input and output files. The most important ones are listed here,
\r
180 the variables for the intermediate files are declared where they
\r
190 int lnno ; /* current line number */
\r
192 @ A set of basic reporting functions.
\r
195 static void mpx_printf(MPX mpx, char *header, char *msg, va_list ap) {
\r
196 fprintf(mpx->errfile, "makempx %s: %s:", header, mpx->mpname);
\r
198 fprintf(mpx->errfile, "%d:", mpx->lnno);
\r
199 fprintf(mpx->errfile, " ");
\r
200 vfprintf(mpx->errfile, msg, ap);
\r
201 fprintf(mpx->errfile, "\n");
\r
205 static void mpx_report(MPX mpx, char *msg, ...) {
\r
207 if (!mpx->debug) return;
\r
209 mpx_printf(mpx, "debug", msg, ap);
\r
211 if ( mpx->history < mpx_warning_given )
\r
212 mpx->history=mpx_cksum_trouble;
\r
216 static void mpx_warn(MPX mpx, char *msg, ...) {
\r
219 mpx_printf(mpx, "warning", msg, ap);
\r
221 if ( mpx->history < mpx_warning_given )
\r
222 mpx->history=mpx_cksum_trouble;
\r
226 static void mpx_error(MPX mpx, char *msg, ...) {
\r
229 mpx_printf(mpx, "error", msg, ap);
\r
231 mpx->history=mpx_warning_given;
\r
234 @ The program uses a |jump_buf| to handle non-local returns,
\r
235 this is initialized at a single spot: the start of |mp_makempx|.
\r
237 @d mpx_jump_out longjmp(mpx->jump_buf,1)
\r
244 static void mpx_abort(MPX mpx, char *msg, ...) {
\r
247 mpx_printf(mpx, "fatal", msg, ap);
\r
249 mpx->history=mpx_fatal_error;
\r
254 @ @<Install and test the non-local jump buffer@>=
\r
255 if (setjmp(mpx->jump_buf) != 0) {
\r
256 int h = mpx->history;
\r
258 xfree(mpx->maincmd);
\r
259 xfree(mpx->mpname);
\r
260 xfree(mpx->mpxname);
\r
266 static FILE *mpx_xfopen (MPX mpx, char *fname, char *fmode) {
\r
267 FILE *f = fopen(fname,fmode);
\r
269 mpx_abort(mpx,"File open error for %s in mode %s", fname, fmode);
\r
272 static void mpx_fclose (MPX mpx, FILE *file) {
\r
274 (void)fclose(file);
\r
278 @d xfree(A) do { mpx_xfree(A); A=NULL; } while (0)
\r
279 @d xrealloc(P,A,B) mpx_xrealloc(mpx,P,A,B)
\r
280 @d xmalloc(A,B) mpx_xmalloc(mpx,A,B)
\r
281 @d xstrdup(A) mpx_xstrdup(mpx,A)
\r
284 static void mpx_xfree (void *x);
\r
285 static void *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) ;
\r
286 static void *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) ;
\r
287 static char *mpx_xstrdup(MPX mpX, const char *s);
\r
290 @ The |max_size_test| guards against overflow, on the assumption that
\r
291 |size_t| is at least 31bits wide.
\r
293 @d max_size_test 0x7FFFFFFF
\r
296 static void mpx_xfree (void *x) {
\r
297 if (x!=NULL) free(x);
\r
299 static void *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) {
\r
301 if ((max_size_test/size)<nmem) {
\r
302 mpx_abort(mpx,"Memory size overflow");
\r
304 w = realloc (p,(nmem*size));
\r
305 if (w==NULL) mpx_abort(mpx,"Out of Memory");
\r
308 static void *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) {
\r
310 if ((max_size_test/size)<nmem) {
\r
311 mpx_abort(mpx,"Memory size overflow");
\r
313 w = malloc (nmem*size);
\r
314 if (w==NULL) mpx_abort(mpx,"Out of Memory");
\r
317 static char *mpx_xstrdup(MPX mpx, const char *s) {
\r
322 if (w==NULL) mpx_abort(mpx,"Out of Memory");
\r
325 @* The command 'newer' became a function.
\r
327 We may have high-res timers in struct stat. If we do, use them.
\r
330 static int mpx_newer(char *source, char *target) {
\r
331 struct stat source_stat, target_stat;
\r
332 #if HAVE_SYS_STAT_H
\r
333 if (stat(target, &target_stat) < 0) return 0; /* true */
\r
334 if (stat(source, &source_stat) < 0) return 1; /* false */
\r
336 if (source_stat.st_mtim.tv_sec > target_stat.st_mtim.tv_sec ||
\r
337 (source_stat.st_mtim.tv_sec == target_stat.st_mtim.tv_sec &&
\r
338 source_stat.st_mtim.tv_nsec >+ target_stat.st_mtim.tv_nsec))
\r
341 if (source_stat.st_mtime >= target_stat.st_mtime)
\r
350 @* Extracting data from \MP\ input.
\r
352 This part of the program transforms a \MP\ input file into a \TeX\ or
\r
353 troff input file by stripping out \.{btex}$\ldots$\.{etex} and
\r
354 \.{verbatimtex}$\ldots$\.{etex} sections.
\r
355 Leading and trailing spaces and tabs are removed from the
\r
356 extracted material and it is surrounded by the preceding and following
\r
357 strings defined immediately below. The input file should be given as
\r
358 argument 1 and the resulting \TeX\ or troff file is written on standard
\r
361 John Hobby wrote the original version, which has since been
\r
362 extensively altered. The current implementation is a bit trickier
\r
363 than I would like, but changing it will take careful study and
\r
364 will likely make it run slower, so I've left it as-is for now.
\r
367 int texcnt ; /* btex..etex blocks so far */
\r
368 int verbcnt ; /* verbatimtex..etex blocks so far */
\r
369 char *bb, *tt, *aa; /* start of before, token, and after strings */
\r
370 char *buf; /* the input line */
\r
373 @ @<Set initial values@>=
\r
374 mpx->bufsize = 1000;
\r
376 @ This function returns NULL on EOF, otherwise it returns |buf|.
\r
377 TODO: It fails to detect a partial last line (missing newline)
\r
380 static char *mpx_getline(MPX mpx, FILE *mpfile) {
\r
383 if (mpx->buf==NULL)
\r
384 mpx->buf = xmalloc(mpx->bufsize,1);
\r
385 while ((c = getc(mpfile)) != EOF && c != '\n' && c != '\r') {
\r
386 mpx->buf[loc++] = c;
\r
387 if (loc == mpx->bufsize) {
\r
388 char *temp = mpx->buf;
\r
389 unsigned n = mpx->bufsize + (mpx->bufsize>>4);
\r
391 mpx_abort(mpx,"Line is too long");
\r
392 mpx->buf = xmalloc(n,1);
\r
393 memcpy(mpx->buf,temp,mpx->bufsize);
\r
411 @ Return nonzero if a prefix of string $s$ matches the null-terminated string $t$
\r
412 and the next character is not a letter or an underscore.
\r
415 static int mpx_match_str(char *s, char *t) {
\r
422 if ((*s>= 'a' && *s<='z') || (*s>= 'A' && *s<='Z') || *s == '_')
\r
428 @ This function tries to express $s$ as the concatenation of three
\r
429 strings $b$, $t$, $a$, with the global pointers $bb$, $tt$, and $aa$ set to the
\r
430 start of the corresponding strings. String $t$ is either a quote mark,
\r
431 a percent sign, or an alphabetic token \.{btex}, \.{etex}, or
\r
432 \.{verbatimtex}. (An alphabetic token is a maximal sequence of letters
\r
433 and underscores.) If there are several possible substrings $t$, we
\r
434 choose the leftmost one. If there is no such $t$, we set $b=s$ and return 0.
\r
437 static int mpx_getbta(MPX mpx, char *s) {
\r
438 int ok = 1; /* zero if last character was |a-z|, |A-Z|, or |_| */
\r
440 for (mpx->tt = mpx->bb; *(mpx->tt) != 0; mpx->tt++) {
\r
441 switch (*(mpx->tt)) {
\r
444 mpx->aa = mpx->tt + 1;
\r
447 if (ok && mpx_match_str(mpx->tt, "btex")) {
\r
448 mpx->aa = mpx->tt + 4;
\r
454 if (ok && mpx_match_str(mpx->tt, "etex")) {
\r
455 mpx->aa = mpx->tt + 4;
\r
461 if (ok && mpx_match_str(mpx->tt, "verbatimtex")) {
\r
462 mpx->aa = mpx->tt + 11;
\r
468 if ((*(mpx->tt) >= 'a' && *(mpx->tt) <= 'z') ||
\r
469 (*(mpx->tt) >= 'A' && *(mpx->tt) <= 'Z') ||
\r
470 (*(mpx->tt) == '_'))
\r
481 static void mpx_copy_mpto (MPX mpx, FILE *outfile) {
\r
482 char *s; /* where a string to print stops */
\r
483 char *t; /* for finding start of last line */
\r
487 if (*mpx->aa == 0) {
\r
488 if ((mpx->aa = mpx_getline(mpx,mpx->mpfile)) == NULL) {
\r
489 mpx_error(mpx,"btex section does not end");
\r
492 if (mpx_getbta(mpx, mpx->aa) && *(mpx->tt) == 'e') {
\r
495 if (*(mpx->tt) == 'b')
\r
496 mpx_error(mpx,"btex in TeX mode");
\r
497 if (*(mpx->tt) == 'v')
\r
498 mpx_error(mpx,"verbatimtex in TeX mode");
\r
504 res = xmalloc(strlen(mpx->bb)+2,1);
\r
505 res = strncpy(res,mpx->bb,(strlen(mpx->bb)+1));
\r
507 res = xrealloc(res,strlen(res)+strlen(mpx->bb)+2,1);
\r
508 res = strncat(res,mpx->bb, strlen(mpx->bb));
\r
511 res = strncat(res, "\n", 1);
\r
513 } while (*(mpx->tt) != 'e');
\r
514 /* whitespace at the end */
\r
515 for (s = res + strlen(res) - 1;
\r
516 s >= res && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'); s--);
\r
519 /* whitespace at the start */
\r
521 s < (res + strlen(res)) && (*s == ' ' || *s == '\t' || *s == '\r'
\r
522 || *s == '\n'); s++);
\r
523 for (; *t != '\n' && t > s; t--);
\r
524 fprintf(outfile,"%s", s);
\r
525 /* put no |%| at end if it's only 1 line total, starting with |%|;
\r
526 * this covers the special case |%&format| in a single line. */
\r
527 if (t != s || *t != '%')
\r
528 fprintf(outfile,"%%");
\r
533 @ Static strings for mpto
\r
536 static const char *mpx_predoc[] = {"", ".po 0\n"};
\r
537 static const char *mpx_postdoc[] = { "\\end{document}\n", ""};
\r
538 static const char *mpx_pretex1[] = {
\r
539 "\\gdef\\mpxshipout{\\shipout\\hbox\\bgroup%\n"
\r
540 " \\setbox0=\\hbox\\bgroup}%\n"
\r
541 "\\gdef\\stopmpxshipout{\\egroup"
\r
542 " \\dimen0=\\ht0 \\advance\\dimen0\\dp0\n"
\r
543 " \\dimen1=\\ht0 \\dimen2=\\dp0\n"
\r
544 " \\setbox0=\\hbox\\bgroup\n"
\r
546 " \\ifnum\\dimen0>0 \\vrule width1sp height\\dimen1 depth\\dimen2 \n"
\r
547 " \\else \\vrule width1sp height1sp depth0sp\\relax\n"
\r
549 " \\ht0=0pt \\dp0=0pt \\box0 \\egroup}\n"
\r
550 "\\mpxshipout%% line %d %s\n", ".lf %d %s\n" };
\r
551 static const char *mpx_pretex[] = { "\\mpxshipout%% line %d %s\n", ".bp\n.lf %d %s\n" };
\r
552 static const char *mpx_posttex[] = { "\n\\stopmpxshipout\n", "\n" };
\r
553 static const char *mpx_preverb1[] = {"", ".lf %d %s\n" }; /* if very first instance */
\r
554 static const char *mpx_preverb[] = { "%% line %d %s\n", ".lf %d %s\n"}; /* all other instances */
\r
555 static const char *mpx_postverb[] = { "\n", "\n" } ;
\r
558 static void mpx_mpto(MPX mpx, char *tmpname, char *mptexpre) {
\r
560 int mode = mpx->mode;
\r
561 char *mpname = mpx->mpname;
\r
562 if (mode==mpx_tex_mode) {
\r
563 TMPNAME_EXT(mpx->tex,".tex");
\r
565 TMPNAME_EXT(mpx->tex,".i");
\r
567 outfile = mpx_xfopen(mpx,mpx->tex, "wb");
\r
568 if (mode==mpx_tex_mode) {
\r
570 if ((fr = fopen(mptexpre, "r"))!= NULL) {
\r
571 while (mpx_getline(mpx, fr) != NULL)
\r
572 fputs(mpx->buf, outfile);
\r
573 mpx_fclose(mpx,fr);
\r
576 mpx->mpfile = mpx_xfopen(mpx,mpname, "r");
\r
577 fprintf(outfile,"%s", mpx_predoc[mode]);
\r
578 while (mpx_getline(mpx, mpx->mpfile) != NULL)
\r
580 fprintf(outfile,"%s", mpx_postdoc[mode]);
\r
581 mpx_fclose(mpx,mpx->mpfile);
\r
582 mpx_fclose(mpx,outfile);
\r
589 mpx->aa = mpx->buf;
\r
590 while (mpx_getbta(mpx, mpx->aa)) {
\r
591 if (*(mpx->tt) == '%') {
\r
593 } else if (*(mpx->tt) == '"') {
\r
595 if (!mpx_getbta(mpx, mpx->aa))
\r
596 mpx_error(mpx,"string does not end");
\r
597 } while (*(mpx->tt) != '"');
\r
598 } else if (*(mpx->tt) == 'b') {
\r
599 if (mpx->texcnt++ == 0)
\r
600 fprintf(outfile,mpx_pretex1[mode], mpx->lnno, mpname);
\r
602 fprintf(outfile,mpx_pretex[mode], mpx->lnno, mpname);
\r
603 mpx_copy_mpto(mpx, outfile);
\r
604 fprintf(outfile,"%s", mpx_posttex[mode]);
\r
605 } else if (*(mpx->tt) == 'v') {
\r
606 if (mpx->verbcnt++ == 0 && mpx->texcnt == 0)
\r
607 fprintf(outfile,mpx_preverb1[mode], mpx->lnno, mpname);
\r
609 fprintf(outfile,mpx_preverb[mode], mpx->lnno, mpname);
\r
610 mpx_copy_mpto(mpx, outfile);
\r
611 fprintf(outfile,"%s", mpx_postverb[mode]);
\r
613 mpx_error(mpx,"unmatched etex");
\r
618 @ @<Run |mpto| on the mp file@>=
\r
619 mpx_mpto(mpx, tmpname, mpxopt->mptexpre)
\r
621 @* DVItoMP Processing.
\r
623 The \.{DVItoMP} program reads binary device-independent (``\.{DVI}'')
\r
624 files that are produced by document compilers such as \TeX, and converts them
\r
625 into a symbolic form understood by \MP. It is loosely based on the \.{DVItype}
\r
626 utility program that produces a more faithful symbolic form of a \.{DVI} file.
\r
628 The output file is a sequence of \MP\ picture expressions, one for every page
\r
629 in the \.{DVI} file. It makes no difference to \.{DVItoMP} where the \.{DVI}
\r
630 file comes from, but it is intended to process the result of running \TeX\
\r
631 or \LaTeX\ on the output of the extraction process that is defined above.
\r
632 Such a \.{DVI} file will contain one page for every \.{btex}$\ldots$\.{etex}
\r
633 block in the original input. Processing with \.{DVItoMP} creates a
\r
634 corresponding sequence of \MP\ picture expressions for use as an auxiliary
\r
635 input file. Since \MP\ expects such files to have the extension \.{.MPX},
\r
636 the output of \.{DVItoMP} is sometimes called an ``\.{MPX}'' file.
\r
638 @ The following parameters can be changed at compile time to extend or
\r
639 reduce \.{DVItoMP}'s capacity.
\r
641 @d virtual_space 1000000 /* maximum total bytes of typesetting commands for virtual fonts */
\r
642 @d max_fonts 1000 /* maximum number of distinct fonts per \.{DVI} file */
\r
643 @d max_fnums 3000 /* maximum number of fonts plus fonts local to virtual fonts */
\r
644 @d max_widths (256*max_fonts) /* maximum number of different characters among all fonts */
\r
645 @d line_length 79 /* maximum output line length (must be at least 60) */
\r
646 @d stack_size 100 /* \.{DVI} files shouldn't |push| beyond this depth */
\r
647 @d font_tolerance 0.00001
\r
648 /* font sizes should match to within this multiple of $2^{20}$ \.{DVI} units */
\r
650 @ If the \.{DVI} file is badly malformed, the whole process must be aborted;
\r
651 \.{DVItoMP} will give up, after issuing an error message about the symptoms
\r
654 @d bad_dvi(A) mpx_abort(mpx,"Bad DVI file: " A "!")
\r
655 @d bad_dvi_two(A,B) mpx_abort(mpx,"Bad DVI file: %s !", A, B)
\r
658 @* The character set.
\r
660 Like all programs written with the \.{WEB} system, \.{DVItoMP} can be
\r
661 used with any character set. It an identify transfrom internally, because
\r
662 the programming for portable input-output is easier when a fixed internal
\r
663 code is used, and because \.{DVI} files use ASCII code for file names.
\r
665 In the conversion from Pascal to C, the |xchr| array has been removed.
\r
666 Because some systems may still want to change the input--output character
\r
667 set, the accesses to |xchr| and |printable| are replaced by macro calls.
\r
669 @d printable(c) (isprint(c) && c < 128 && c!='"')
\r
673 static void mpx_open_mpxfile (MPX mpx) { /* prepares to write text on |mpxfile| */
\r
674 mpx->mpxfile = mpx_xfopen (mpx,mpx->mpxname, "wb");
\r
677 @* Device-independent file format.
\r
678 The format of \.{DVI} files is described in many places including
\r
679 \.{dvitype.web} and Volume~B of D.~E. Knuth's {\sl Computers and Typesetting}.
\r
680 This program refers to the following command codes.
\r
682 @d id_byte 2 /* identifies the kind of \.{DVI} files described here */
\r
684 @d set_char_0 0 /* typeset character 0 and move right */
\r
685 @d set1 128 /* typeset a character and move right */
\r
686 @d set_rule 132 /* typeset a rule and move right */
\r
687 @d put1 133 /* typeset a character */
\r
688 @d put_rule 137 /* typeset a rule */
\r
689 @d nop 138 /* no operation */
\r
690 @d bop 139 /* beginning of page */
\r
691 @d eop 140 /* ending of page */
\r
692 @d push 141 /* save the current positions */
\r
693 @d pop 142 /* restore previous positions */
\r
694 @d right1 143 /* move right */
\r
695 @d w0 147 /* move right by |w| */
\r
696 @d w1 148 /* move right and set |w| */
\r
697 @d x0 152 /* move right by |x| */
\r
698 @d x1 153 /* move right and set |x| */
\r
699 @d down1 157 /* move down */
\r
700 @d y0 161 /* move down by |y| */
\r
701 @d y1 162 /* move down and set |y| */
\r
702 @d z0 166 /* move down by |z| */
\r
703 @d z1 167 /* move down and set |z| */
\r
704 @d fnt_num_0 171 /* set current font to 0 */
\r
705 @d fnt1 235 /* set current font */
\r
706 @d xxx1 239 /* extension to \.{DVI} primitives */
\r
707 @d xxx4 242 /* potentially long extension to \.{DVI} primitives */
\r
708 @d fnt_def1 243 /* define the meaning of a font number */
\r
709 @d pre 247 /* preamble */
\r
710 @d post 248 /* postamble beginning */
\r
711 @d post_post 249 /* postamble ending */
\r
712 @d undefined_commands 250: case 251: case 252: case 253: case 254: case 255
\r
714 @* Input from binary files.
\r
716 @ The program deals with two binary file variables: |dvi_file| is the main
\r
717 input file that we are translating into symbolic form, and |tfm_file| is
\r
718 the current font metric file from which character-width information is
\r
719 being read. It is convenient to have a throw-away variable for function
\r
720 results when reading parts of the files that are being skipped.
\r
723 FILE * dvi_file; /* the input file */
\r
724 FILE * tfm_file; /* a font metric file */
\r
725 FILE * vf_file; /* a virtual font file */
\r
727 @ Prepares to read packed bytes in |dvi_file|
\r
729 static void mpx_open_dvi_file (MPX mpx) {
\r
730 mpx->dvi_file = fopen(mpx->dviname,"rb");
\r
731 if (mpx->dvi_file==NULL)
\r
732 mpx_abort(mpx,"DVI generation failed");
\r
735 @ Prepares to read packed bytes in |tfm_file|
\r
737 static boolean mpx_open_tfm_file (MPX mpx) {
\r
738 mpx->tfm_file = mpx_fsearch(mpx, mpx->cur_name, mpx_tfm_format);
\r
739 if (mpx->tfm_file == NULL)
\r
740 mpx_abort(mpx,"Cannot find TFM %s", mpx->cur_name);
\r
741 free (mpx->cur_name); /* We |xmalloc|'d this before we got called. */
\r
742 return true; /* If we get here, we succeeded. */
\r
745 @ Prepares to read packed bytes in |vf_file|.
\r
746 It's ok if the \.{VF} file doesn't exist.
\r
749 static boolean mpx_open_vf_file (MPX mpx) {
\r
750 mpx->vf_file = mpx_fsearch(mpx, mpx->cur_name, mpx_vf_format);
\r
751 if (mpx->vf_file) {
\r
752 free (mpx->cur_name);
\r
758 @ If you looked carefully at the preceding code, you probably asked,
\r
759 ``What is |cur_name|?'' Good question. It's a global
\r
760 variable: |cur_name| is a string variable that will be set to the
\r
761 current font metric file name before |open_tfm_file| or |open_vf_file|
\r
765 char *cur_name; /* external name */
\r
767 @ It turns out to be convenient to read four bytes at a time, when we are
\r
768 inputting from \.{TFM} files. The input goes into global variables
\r
769 |b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
\r
773 int b0, b1, b2, b3; /* four bytes input at once */
\r
775 @ The |read_tfm_word| procedure sets |b0| through |b3| to the next
\r
776 four bytes in the current \.{TFM} file.
\r
779 static void mpx_read_tfm_word (MPX mpx) {
\r
780 mpx->b0 = getc(mpx->tfm_file);
\r
781 mpx->b1 = getc(mpx->tfm_file);
\r
782 mpx->b2 = getc(mpx->tfm_file);
\r
783 mpx->b3 = getc(mpx->tfm_file);
\r
786 @ Input can come from from three different sources depending on the settings
\r
787 of global variables. When |vf_reading| is true, we read from the \.{VF} file.
\r
788 Otherwise, input can either come directly from |dvi_file| or from a buffer
\r
789 |cmd_buf|. The latter case applies whenever |buf_ptr<virtual_space|.
\r
792 boolean vf_reading; /* should input come from |vf_file|? */
\r
793 unsigned char cmd_buf[(virtual_space+1)]; /* commands for virtual characters */
\r
794 unsigned int buf_ptr; /* |cmd_buf| index for the next byte */
\r
797 mpx->vf_reading=false;
\r
798 mpx->buf_ptr=virtual_space;
\r
800 @ We shall use a set of simple functions to read the next byte or bytes from the
\r
801 current input source. There are seven possibilities, each of which is treated
\r
802 as a separate function in order to minimize the overhead for subroutine calls.
\r
805 static integer mpx_get_byte (MPX mpx) { /* returns the next byte, unsigned */
\r
807 @<Read one byte into |b|@>;
\r
811 static integer mpx_signed_byte (MPX mpx) { /* returns the next byte, signed */
\r
813 @<Read one byte into |b|@>;
\r
814 return ( b<128 ? b : (b-256));
\r
817 static integer mpx_get_two_bytes (MPX mpx) { /* returns the next two bytes, unsigned */
\r
819 a=0; b=0; /* for compiler warnings */
\r
820 @<Read two bytes into |a| and |b|@>;
\r
821 return (a*(int)(256)+b);
\r
824 static integer mpx_signed_pair (MPX mpx) { /* returns the next two bytes, signed */
\r
826 a=0; b=0; /* for compiler warnings */
\r
827 @<Read two bytes into |a| and |b|@>;
\r
828 if ( a<128 ) return (a*256+b);
\r
829 else return ((a-256)*256+b);
\r
832 static integer mpx_get_three_bytes (MPX mpx) { /* returns the next three bytes, unsigned */
\r
833 unsigned char a,b,c;
\r
834 a=0; b=0; c=0; /* for compiler warnings */
\r
835 @<Read three bytes into |a|, |b|, and~|c|@>;
\r
836 return ((a*(int)(256)+b)*256+c);
\r
839 static integer mpx_signed_trio (MPX mpx) { /* returns the next three bytes, signed */
\r
840 unsigned char a,b,c;
\r
841 a=0; b=0; c=0; /* for compiler warnings */
\r
842 @<Read three bytes into |a|, |b|, and~|c|@>;
\r
843 if ( a<128 ) return ((a*(int)(256)+b)*256+c);
\r
844 else return (((a-(int)(256))*256+b)*256+c);
\r
847 static integer mpx_signed_quad (MPX mpx) { /* returns the next four bytes, signed */
\r
848 unsigned char a,b,c,d;
\r
849 a=0; b=0; c=0; d=0; /* for compiler warnings */
\r
850 @<Read four bytes into |a|, |b|, |c|, and~|d|@>;
\r
851 if ( a<128 ) return (((a*(int)(256)+b)*256+c)*256+d);
\r
852 else return ((((a-256)*(int)(256)+b)*256+c)*256+d);
\r
855 @ @<Read one byte into |b|@>=
\r
856 if ( mpx->vf_reading ) {
\r
857 b = getc(mpx->vf_file);
\r
858 } else if ( mpx->buf_ptr==virtual_space ) {
\r
859 b = getc(mpx->dvi_file);
\r
861 b=mpx->cmd_buf[mpx->buf_ptr];
\r
862 incr(mpx->buf_ptr);
\r
865 @ @<Read two bytes into |a| and |b|@>=
\r
866 if ( mpx->vf_reading ) {
\r
867 a = getc(mpx->vf_file);
\r
868 b = getc(mpx->vf_file);
\r
869 } else if ( mpx->buf_ptr==virtual_space ) {
\r
870 a = getc(mpx->dvi_file);
\r
871 b = getc(mpx->dvi_file);
\r
872 } else if ( mpx->buf_ptr+2>mpx->n_cmds ) {
\r
873 mpx_abort(mpx,"Error detected while interpreting a virtual font");
\r
874 @.Error detected while...@>
\r
876 a=mpx->cmd_buf[mpx->buf_ptr];
\r
877 b=mpx->cmd_buf[mpx->buf_ptr+1];
\r
881 @ @<Read three bytes into |a|, |b|, and~|c|@>=
\r
882 if ( mpx->vf_reading ) {
\r
883 a = getc(mpx->vf_file);
\r
884 b = getc(mpx->vf_file);
\r
885 c = getc(mpx->vf_file);
\r
886 } else if ( mpx->buf_ptr==virtual_space ) {
\r
887 a = getc(mpx->dvi_file);
\r
888 b = getc(mpx->dvi_file);
\r
889 c = getc(mpx->dvi_file);
\r
890 } else if ( mpx->buf_ptr+3>mpx->n_cmds ) {
\r
891 mpx_abort(mpx,"Error detected while interpreting a virtual font");
\r
892 @.Error detected while...@>
\r
894 a=mpx->cmd_buf[mpx->buf_ptr];
\r
895 b=mpx->cmd_buf[mpx->buf_ptr+1];
\r
896 c=mpx->cmd_buf[mpx->buf_ptr+2];
\r
900 @ @<Read four bytes into |a|, |b|, |c|, and~|d|@>=
\r
901 if ( mpx->vf_reading ) {
\r
902 a = getc(mpx->vf_file);
\r
903 b = getc(mpx->vf_file);
\r
904 c = getc(mpx->vf_file);
\r
905 d = getc(mpx->vf_file);
\r
906 } else if ( mpx->buf_ptr==virtual_space ) {
\r
907 a = getc(mpx->dvi_file);
\r
908 b = getc(mpx->dvi_file);
\r
909 c = getc(mpx->dvi_file);
\r
910 d = getc(mpx->dvi_file);
\r
911 } else if ( mpx->buf_ptr+4>mpx->n_cmds ) {
\r
912 mpx_abort(mpx,"Error detected while interpreting a virtual font");
\r
913 @.Error detected while...@>
\r
915 a=mpx->cmd_buf[mpx->buf_ptr];
\r
916 b=mpx->cmd_buf[mpx->buf_ptr+1];
\r
917 c=mpx->cmd_buf[mpx->buf_ptr+2];
\r
918 d=mpx->cmd_buf[mpx->buf_ptr+3];
\r
922 @* Data structures for fonts.
\r
924 \.{DVI} file format does not include information about character widths, since
\r
925 that would tend to make the files a lot longer. But a program that reads
\r
926 a \.{DVI} file is supposed to know the widths of the characters that appear
\r
927 in \\{set\_char} commands. Therefore \.{DVItoMP} looks at the font metric
\r
928 (\.{TFM}) files for the fonts that are involved.
\r
929 @.TFM {\rm files}@>
\r
931 @ For purposes of this program, the only thing we need to know about a
\r
932 given character |c| in a non-virtual font |f| is the width. For the font as
\r
933 a whole, all we need is the symbolic name to use in the \.{MPX} file.
\r
935 This information appears implicitly in the following data
\r
936 structures. The current number of fonts defined is |nf|. Each such font has
\r
937 an internal number |f|, where |0<=f<nf|. There is also an external number
\r
938 that identifies the font in the \.{DVI} file. The correspondence is
\r
939 maintained in arrays |font_num| and |internal_num| so that |font_num[i]|
\r
940 is the external number for |f=internal_num[i]|.
\r
941 The external name of this font is the string that occupies |font_name[f]|.
\r
942 The legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive.
\r
943 The \.{TFM} file can specify that some of these are invalid, but this doesn't
\r
944 concern \.{DVItoMP} because it does not do extensive error checking.
\r
945 The width of character~|c| in font~|f| is given by
\r
946 |char_width(f,c)=width[info_base[f]+c]|, and |info_ptr| is the
\r
947 first unused position of the |width| array.
\r
949 If font~|f| is a virtual font, there is a list of \.{DVI} commands for each
\r
950 character. These occupy consecutive positions in the |cmd_buf| array with
\r
951 the commands for character~|c| starting at
\r
952 |start_cmd(f,c)=cmd_ptr[info_base[f]+c]| and ending just before
\r
953 |start_cmd(f,c+1)|. Font numbers used when interpreting these \.{DVI}
\r
954 commands occupy positions |fbase[f]| through |ftop[f]-1| in the |font_num|
\r
955 table and the |internal_num| array gives the corresponding internal font
\r
956 numbers. If such an internal font number~|i| does not correspond to
\r
957 some font occuring in the \.{DVI} file, then |font_num[i]| has not been
\r
958 assigned a meaningful value; this is indicated by |local_only[i]=true|.
\r
960 If font~|f| is not virtual, then |fbase[f]=0| and |ftop[f]=0|. The |start_cmd|
\r
961 values are ignored in this case.
\r
963 @d char_width(A,B) mpx->width[mpx->info_base[(A)]+(B)]
\r
964 @d start_cmd(A,B) mpx->cmd_ptr[mpx->info_base[(A)]+(B)]
\r
967 integer font_num[(max_fnums+1)]; /* external font numbers */
\r
968 integer internal_num[(max_fnums+1)]; /* internal font numbers */
\r
969 boolean local_only[(max_fnums+1)]; /* |font_num| meaningless? */
\r
970 char *font_name[(max_fonts+1)]; /* starting positions of external font names */
\r
971 double font_scaled_size[(max_fonts+1)]; /* scale factors over $2^{20}$ */
\r
972 double font_design_size[(max_fonts+1)]; /* design sizes over $2^{20}$ */
\r
973 integer font_check_sum[(max_fonts+1)]; /* check sum from the |font_def| */
\r
974 integer font_bc[(max_fonts+1)]; /* beginning characters in fonts */
\r
975 integer font_ec[(max_fonts+1)]; /* ending characters in fonts */
\r
976 integer info_base[(max_fonts+1)]; /* index into |width| and |cmd_ptr| tables */
\r
977 integer width[(max_widths+1)];
\r
978 /* character widths, in units $2^{-20}$ of design size */
\r
979 integer fbase[(max_fonts+1)]; /* index into |font_num| for local fonts */
\r
980 integer ftop[(max_fonts+1)]; /* |font_num| index where local fonts stop */
\r
981 integer cmd_ptr[(max_widths+1)]; /* starting positions in |cmd_buf| */
\r
982 unsigned int nfonts; /* the number of known fonts */
\r
983 unsigned int vf_ptr; /* next |font_num| entry for virtual font font tables */
\r
984 unsigned int info_ptr; /* allocation pointer for |width| and |cmd_ptr| tables */
\r
985 unsigned int n_cmds; /* number of occupied cells in |cmd_buf| */
\r
986 unsigned int cur_fbase, cur_ftop;
\r
987 /* currently applicable part of the |font_num| table */
\r
990 mpx->nfonts=0; mpx->info_ptr=0; mpx->font_name[0]=0;
\r
991 mpx->vf_ptr=max_fnums;
\r
992 mpx->cur_fbase=0; mpx->cur_ftop=0;
\r
994 @ Printing the name of a given font is easy except that a procedure |print_char|
\r
995 is needed to actually send an |ASCII_code| to the \.{MPX} file.
\r
997 @c @<Declare subroutines for printing strings@>@;
\r
998 static void mpx_print_font (MPX mpx, integer f) { /* |f| is an internal font number */
\r
999 if ( (f<0)||(f>=(int)mpx->nfonts) ) {
\r
1000 bad_dvi("Undefined font");
\r
1002 char *s = mpx->font_name[f];
\r
1004 mpx_print_char(mpx,*s);
\r
1010 @ Sometimes a font name is needed as part of an error message.
\r
1012 @d font_warn(A,B) mpx_warn (mpx,"%s %s",A,mpx->font_name[(B)])
\r
1013 @d font_error(A,B) mpx_error(mpx,"%s %s",A,mpx->font_name[(B)])
\r
1014 @d font_abort(A,B) mpx_abort(mpx,"%s %s",A,mpx->font_name[(B)])
\r
1017 @ When we encounter a font definition, we save the name, checksum, and size
\r
1018 information, but we don't actually read the \.{TFM} or \.{VF} file until we
\r
1019 are about to use the font. If a matching font is not already defined, we then
\r
1020 allocate a new internal font number.
\r
1022 The following subroutine does the necessary things when a \\{fnt\_def} command
\r
1023 is encountered in the \.{DVI} file or in a \.{VF} file. It assumes that the
\r
1024 first argument has already been parsed and is given by the parameter~|e|.
\r
1026 @c @<Declare a function called |match_font|@>@;
\r
1027 static void mpx_define_font (MPX mpx, integer e) { /* |e| is an external font number */
\r
1028 integer i; /* index into |font_num| and |internal_num| */
\r
1029 integer n; /* length of the font name and area */
\r
1030 integer k; /* general purpose loop counter */
\r
1031 integer x; /* a temporary value for scaled size computation */
\r
1032 if ( mpx->nfonts==max_fonts )
\r
1033 mpx_abort(mpx,"DVItoMP capacity exceeded (max fonts=%d)!", max_fonts);
\r
1034 @.DVItoMP capacity exceeded...@>
\r
1035 @<Allocate an index |i| into the |font_num| and |internal_num| tables@>;
\r
1036 @<Read the font parameters into position for font |nf|@>;
\r
1037 mpx->internal_num[i]=mpx_match_font(mpx, mpx->nfonts,true);
\r
1038 if ( mpx->internal_num[i]==(int)mpx->nfonts ) {
\r
1039 mpx->info_base[mpx->nfonts]=max_widths; /* indicate that the info isn't loaded yet */
\r
1040 mpx->local_only[mpx->nfonts]=mpx->vf_reading; incr(mpx->nfonts);
\r
1044 @ @<Allocate an index |i| into the |font_num| and |internal_num| tables@>=
\r
1045 if ( mpx->vf_ptr==mpx->nfonts )
\r
1046 mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums);
\r
1047 @.DVItoMP capacity exceeded...@>
\r
1048 if ( mpx->vf_reading ) {
\r
1049 mpx->font_num[mpx->nfonts]=0; i=mpx->vf_ptr; decr(mpx->vf_ptr);
\r
1053 mpx->font_num[i]=e
\r
1055 @ @<Read the font parameters into position for font |nf|@>=
\r
1056 mpx->font_check_sum[mpx->nfonts]=mpx_signed_quad(mpx);
\r
1057 @<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>;
\r
1058 n=mpx_get_byte(mpx); /* that is the area */
\r
1059 n=n+mpx_get_byte(mpx);
\r
1060 mpx->font_name[mpx->nfonts]=xmalloc(n+1,1);
\r
1062 mpx->font_name[mpx->nfonts][k]=mpx_get_byte(mpx);
\r
1063 mpx->font_name[mpx->nfonts][k]=0
\r
1065 @ The scaled size and design size are stored in \.{DVI} units divided by $2^{20}$.
\r
1066 The units for scaled size are a little different if we are reading a virtual
\r
1067 font, but this will be corrected when the scaled size is used. The scaled size
\r
1068 also needs to be truncated to at most 23 significant bits in order to make
\r
1069 the character width calculation match what \TeX\ does.
\r
1071 @<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>=
\r
1072 x=mpx_signed_quad(mpx);
\r
1074 while ( mpx->x>040000000 ) {
\r
1077 mpx->font_scaled_size[mpx->nfonts]=x*k/1048576.0;
\r
1078 if ( mpx->vf_reading )
\r
1079 mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)*mpx->dvi_per_fix/1048576.0;
\r
1080 else mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)/1048576.0;
\r
1083 double dvi_per_fix; /* converts points scaled $2^{20}$ to \.{DVI} units */
\r
1085 @ The |match_font| function tries to find a match for the font with internal
\r
1086 number~|ff|, returning |nf| or the number of the matching font. If
\r
1087 |exact=true|, the name and scaled size should match. Otherwise the scaled
\r
1088 size need not match but the font found must be already loaded, not just
\r
1091 @<Declare a function called |match_font|@>=
\r
1092 static integer mpx_match_font (MPX mpx, unsigned ff, boolean exact) {
\r
1093 unsigned f; /* font number being tested */
\r
1094 for (f=0; f<mpx->nfonts ; f++) {
\r
1096 @<Compare the names of fonts |f| and |ff|; |continue| if they differ@>;
\r
1098 if ( fabs(mpx->font_scaled_size[f]-mpx->font_scaled_size[ff])<= font_tolerance ) {
\r
1099 if ( ! mpx->vf_reading ) {
\r
1100 if ( mpx->local_only[f] ) {
\r
1101 mpx->font_num[f]=mpx->font_num[ff]; mpx->local_only[f]=false;
\r
1102 } else if ( mpx->font_num[f]!=mpx->font_num[ff] ) {
\r
1108 } else if ( mpx->info_base[f]!=max_widths ) {
\r
1113 if ( f<mpx->nfonts ) {
\r
1114 @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>;
\r
1119 @ @<Compare the names of fonts |f| and |ff|; |continue| if they differ@>=
\r
1120 if (strcmp(mpx->font_name[f],mpx->font_name[ff]))
\r
1123 @ @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>=
\r
1124 if ( fabs(mpx->font_design_size[f]-mpx->font_design_size[ff]) > font_tolerance ) {
\r
1125 font_error("Inconsistent design sizes given for ",ff);
\r
1126 @.Inconsistent design sizes@>
\r
1127 } else if ( mpx->font_check_sum[f]!=mpx->font_check_sum[ff] ) {
\r
1128 font_warn("Checksum mismatch for ", ff);
\r
1129 @.Checksum mismatch@>
\r
1132 @* Reading ordinary fonts.
\r
1133 An auxiliary array |in_width| is used to hold the widths as they are
\r
1134 input. The global variable |tfm_check_sum| is set to the check sum that
\r
1135 appears in the current \.{TFM} file.
\r
1138 integer in_width[256]; /* \.{TFM} width data in \.{DVI} units */
\r
1139 integer tfm_check_sum; /* check sum found in |tfm_file| */
\r
1141 @ Here is a procedure that absorbs the necessary information from a
\r
1142 \.{TFM} file, assuming that the file has just been successfully reset
\r
1143 so that we are ready to read its first byte. (A complete description of
\r
1144 \.{TFM} file format appears in the documentation of \.{TFtoPL} and will
\r
1145 not be repeated here.) The procedure does not check the \.{TFM} file
\r
1146 for validity, nor does it give explicit information about what is
\r
1147 wrong with a \.{TFM} file that proves to be invalid. The procedure simply
\r
1148 aborts the program if it detects anything amiss in the \.{TFM} data.
\r
1151 static void mpx_in_TFM (MPX mpx,integer f) {
\r
1152 /* input \.{TFM} data for font |f| or abort */
\r
1153 integer k; /* index for loops */
\r
1154 int lh; /* length of the header data, in four-byte words */
\r
1155 int nw; /* number of words in the width table */
\r
1156 int wp; /* new value of |info_ptr| after successful input */
\r
1157 @<Read past the header data; |abort| if there is a problem@>;
\r
1158 @<Store character-width indices at the end of the |width| table@>;
\r
1159 @<Read the width values into the |in_width| table@>;
\r
1160 @<Move the widths from |in_width| to |width|@>;
\r
1161 mpx->fbase[f]=0; mpx->ftop[f]=0;
\r
1166 @ @<Read past the header...@>=
\r
1167 mpx_read_tfm_word(mpx); lh=mpx->b2*(int)(256)+mpx->b3;
\r
1168 mpx_read_tfm_word(mpx);
\r
1169 mpx->font_bc[f]=mpx->b0*(int)(256)+mpx->b1;
\r
1170 mpx->font_ec[f]=mpx->b2*(int)(256)+mpx->b3;
\r
1171 if ( mpx->font_ec[f]<mpx->font_bc[f] ) mpx->font_bc[f]=mpx->font_ec[f]+1;
\r
1172 if ( mpx->info_ptr+mpx->font_ec[f]-mpx->font_bc[f]+1>max_widths )
\r
1173 mpx_abort(mpx,"DVItoMP capacity exceeded (width table size=%d)!",max_widths);
\r
1174 @.DVItoMP capacity exceeded...@>
\r
1175 wp=mpx->info_ptr+mpx->font_ec[f]-mpx->font_bc[f]+1;
\r
1176 mpx_read_tfm_word(mpx); nw=mpx->b0*256+mpx->b1;
\r
1177 if ( (nw==0)||(nw>256) )
\r
1178 font_abort("Bad TFM file for ",f);
\r
1180 for (k=1;k<=3+lh;k++) {
\r
1181 if ( feof(mpx->tfm_file) )
\r
1182 font_abort("Bad TFM file for ",f);
\r
1184 mpx_read_tfm_word(mpx);
\r
1186 if ( mpx->b0<128 )
\r
1187 mpx->tfm_check_sum=((mpx->b0*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
\r
1189 mpx->tfm_check_sum=(((mpx->b0-256)*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
\r
1193 @ @<Store character-width indices...@>=
\r
1195 for (k=mpx->info_ptr;k<=wp-1;k++ ) {
\r
1196 mpx_read_tfm_word(mpx);
\r
1197 if ( mpx->b0>nw )
\r
1198 font_abort("Bad TFM file for ",f);
\r
1200 mpx->width[k]=mpx->b0;
\r
1204 @ No fancy width calculation is needed here because \.{DVItoMP} stores
\r
1205 widths in their raw form as multiples of the design size scaled by $2^{20}$.
\r
1206 The |font_scaled_size| entries have been computed so that the final width
\r
1207 compution can be done in floating point if enough precision is available.
\r
1209 @<Read the width values into the |in_width| table@>=
\r
1210 for (k=0;k<=nw-1;k++) {
\r
1211 mpx_read_tfm_word(mpx);
\r
1212 if ( mpx->b0>127 ) mpx->b0=mpx->b0-256;
\r
1213 mpx->in_width[k]=((mpx->b0*0400+mpx->b1)*0400+mpx->b2)*0400+mpx->b3;
\r
1216 @ The width compution uses a scale factor |dvi_scale| that will be introduced
\r
1217 later. It is equal to one when not typesetting a character from a virtual
\r
1218 font. In that case, the following expressions do the width computation that is
\r
1219 so important in \.{DVItype}. It is less important here because it is impractical
\r
1220 to guarantee precise character positioning in \MP\ output. Nevertheless, the
\r
1221 width compution will be precise if reals have at least 46-bit mantissas and
\r
1222 |round(x-.5)| is equivalent to $\lfloor x\rfloor$. It may be a good idea to
\r
1223 modify this computation if these conditions are not met.
\r
1224 @^system dependencies@>
\r
1226 @<Width of character |c| in font |f|@>=
\r
1227 floor(mpx->dvi_scale*mpx->font_scaled_size[f]*char_width(f,c))
\r
1229 @ @<Width of character |p| in font |cur_font|@>=
\r
1230 floor(mpx->dvi_scale*mpx->font_scaled_size[cur_font]*char_width(cur_font,p))
\r
1232 @ @<Move the widths from |in_width| to |width|@>=
\r
1233 if ( mpx->in_width[0]!=0 )
\r
1234 font_abort("Bad TFM file for ",f); /* the first width should be zero */
\r
1236 mpx->info_base[f]=mpx->info_ptr-mpx->font_bc[f];
\r
1238 for (k=mpx->info_ptr;k<=wp-1;k++) {
\r
1239 mpx->width[k]=mpx->in_width[mpx->width[k]];
\r
1244 @* Reading virtual fonts.
\r
1246 The |in_VF| procedure absorbs the necessary information from a \.{VF} file that
\r
1247 has just been reset so that we are ready to read the first byte. (A complete
\r
1248 description of \.{VF} file format appears in the documention of \.{VFtoVP}).
\r
1249 Like |in_TFM|, this procedure simply aborts the program if it detects anything
\r
1250 wrong with the \.{VF} file.
\r
1253 @<Declare a function called |first_par|@>@;
\r
1254 static void mpx_in_VF (MPX mpx, integer f) {
\r
1255 /* read \.{VF} data for font |f| or abort */
\r
1256 integer p; /* a byte from the \.{VF} file */
\r
1257 boolean was_vf_reading; /* old value of |vf_reading| */
\r
1258 integer c; /* the current character code */
\r
1259 integer limit; /* space limitations force character codes to be less than this */
\r
1260 integer w; /* a \.{TFM} width being read */
\r
1261 was_vf_reading=mpx->vf_reading; mpx->vf_reading=true;
\r
1262 @<Start reading the preamble from a \.{VF} file@>;@/
\r
1263 @<Initialize the data structures for the virtual font@>;@/
\r
1264 p=mpx_get_byte(mpx);
\r
1265 while ( mpx->p>=fnt_def1 ) {
\r
1266 if ( mpx->p>fnt_def1+3 )
\r
1267 font_abort("Bad VF file for ",f);
\r
1268 mpx_define_font(mpx, mpx_first_par(mpx, p));
\r
1269 p=mpx_get_byte(mpx);
\r
1271 while ( p<=242 ) {
\r
1272 if ( feof(mpx->vf_file) )
\r
1273 font_abort("Bad VF file for ",f);
\r
1274 @<Read the packet length, character code, and \.{TFM} width@>;
\r
1275 @<Store the character packet in |cmd_buf|@>;
\r
1276 p=mpx_get_byte(mpx);
\r
1278 if ( mpx->p==post ) {
\r
1279 @<Finish setting up the data structures for the new virtual font@>;
\r
1280 mpx->vf_reading=was_vf_reading;
\r
1285 @ @<Start reading the preamble from a \.{VF} file@>=
\r
1286 p=mpx_get_byte(mpx);
\r
1288 font_abort("Bad VF file for ",f);
\r
1289 p=mpx_get_byte(mpx); /* fetch the identification byte */
\r
1291 font_abort("Bad VF file for ",f);
\r
1292 p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */
\r
1294 (void)mpx_get_byte(mpx);
\r
1295 mpx->tfm_check_sum=mpx_signed_quad(mpx);
\r
1296 (void)mpx_signed_quad(mpx); /* skip over the design size */
\r
1298 @ @<Initialize the data structures for the virtual font@>=
\r
1299 mpx->ftop[f]=mpx->vf_ptr;
\r
1300 if ( mpx->vf_ptr==mpx->nfonts )
\r
1301 mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums);
\r
1302 @.DVItoMP capacity exceeded...@>
\r
1303 decr(mpx->vf_ptr);
\r
1304 mpx->info_base[f]=mpx->info_ptr;
\r
1305 limit=max_widths-mpx->info_base[f];@/
\r
1306 mpx->font_bc[f]=limit; mpx->font_ec[f]=0
\r
1308 @ @<Read the packet length, character code, and \.{TFM} width@>=
\r
1310 p=mpx_signed_quad(mpx); c=mpx_signed_quad(mpx); w=mpx_signed_quad(mpx);
\r
1312 font_abort("Bad VF file for ",f);
\r
1314 c=mpx_get_byte(mpx); w=mpx_get_three_bytes(mpx);
\r
1317 mpx_abort(mpx,"DVItoMP capacity exceeded (max widths=%d)", max_widths);
\r
1318 @.DVItoMP capacity exceeded...@>
\r
1319 if ( c<mpx->font_bc[f] ) mpx->font_bc[f]=c;
\r
1320 if ( c>mpx->font_ec[f] ) mpx->font_ec[f]=c;
\r
1323 @ @<Store the character packet in |cmd_buf|@>=
\r
1324 if ( mpx->n_cmds+p>=virtual_space )
\r
1325 mpx_abort(mpx,"DVItoMP capacity exceeded (virtual font space=%d)",virtual_space);
\r
1326 @.DVItoMP capacity exceeded...@>
\r
1327 start_cmd(f,c)=mpx->n_cmds;
\r
1329 mpx->cmd_buf[mpx->n_cmds]=mpx_get_byte(mpx);
\r
1330 incr(mpx->n_cmds); decr(p);
\r
1332 mpx->cmd_buf[mpx->n_cmds]=eop; /* add the end-of-packet marker */
\r
1335 @ There are unused |width| and |cmd_ptr| entries if |font_bc[f]>0| but it isn't
\r
1336 worthwhile to slide everything down just to save a little space.
\r
1338 @<Finish setting up the data structures for the new virtual font@>=
\r
1339 mpx->fbase[f]=mpx->vf_ptr+1;
\r
1340 mpx->info_ptr=mpx->info_base[f]+mpx->font_ec[f]+1
\r
1345 The character width information for a font is loaded when the font is selected
\r
1346 for the first time. This information might already be loaded if the font has
\r
1347 already been used at a different scale factor. Otherwise, we look for a \.{VF}
\r
1348 file, or failing that, a \.{TFM} file. All this is done by the |select_font|
\r
1349 function that takes an external font number~|e| and returns the corresponding
\r
1350 internal font number with the width information loaded.
\r
1353 static integer mpx_select_font (MPX mpx, integer e) {
\r
1354 int f; /* the internal font number */
\r
1355 int ff; /* internal font number for an existing version */
\r
1356 integer k; /* general purpose loop counter */
\r
1357 @<Set |f| to the internal font number that corresponds to |e|,
\r
1358 or |abort| if there is none@>;
\r
1359 if ( mpx->info_base[f]==max_widths ) {
\r
1360 ff=mpx_match_font(mpx, f,false);
\r
1361 if ( ff<(int)mpx->nfonts ) {
\r
1362 @<Make font |f| refer to the width information from font |ff|@>;
\r
1364 @<Move the \.{VF} file name into the |cur_name| string@>;
\r
1365 if ( mpx_open_vf_file(mpx) ) {
\r
1366 mpx_in_VF(mpx, f);
\r
1368 if ( ! mpx_open_tfm_file(mpx) )
\r
1369 font_abort("No TFM file found for ",f);
\r
1370 @.no TFM file found@>
\r
1371 mpx_in_TFM(mpx, f);
\r
1373 @<Make sure the checksum in the font file matches the one given in the
\r
1374 |font_def| for font |f|@>;
\r
1376 @<Do any other initialization required for the new font |f|@>;
\r
1381 @ @<Set |f| to the internal font number that corresponds to |e|,...@>=
\r
1382 if ( mpx->cur_ftop<=mpx->nfonts )
\r
1383 mpx->cur_ftop=mpx->nfonts;
\r
1384 mpx->font_num[mpx->cur_ftop]=e;
\r
1386 while ((mpx->font_num[k]!=e)|| mpx->local_only[k] ) incr(k);
\r
1387 if ( k==(int)mpx->cur_ftop )
\r
1388 mpx_abort(mpx,"Undefined font selected");
\r
1389 f=mpx->internal_num[k]
\r
1391 @ @<Make font |f| refer to the width information from font |ff|@>=
\r
1393 mpx->font_bc[f]=mpx->font_bc[ff];
\r
1394 mpx->font_ec[f]=mpx->font_ec[ff];
\r
1395 mpx->info_base[f]=mpx->info_base[ff];
\r
1396 mpx->fbase[f]=mpx->fbase[ff];
\r
1397 mpx->ftop[f]=mpx->ftop[ff];
\r
1400 @ The string |cur_name| is supposed to be set to the external name of the
\r
1401 \.{VF} file for the current font.
\r
1402 @^system dependencies@>
\r
1404 @<Move the \.{VF} file name into the |cur_name| string@>=
\r
1405 mpx->cur_name = xstrdup (mpx->font_name[f])
\r
1407 @ @<Make sure the checksum in the font file matches the one given in the...@>=
\r
1409 if ( (mpx->font_check_sum[f]!=0)&&(mpx->tfm_check_sum!=0)&&@|
\r
1410 (mpx->font_check_sum[f]!=mpx->tfm_check_sum) ) {
\r
1411 font_warn("Checksum mismatch for ",f);
\r
1412 @.Checksum mismatch@>
\r
1416 @* Low level output routines.
\r
1418 One of the basic output operations is to write a \MP\ string expression for
\r
1419 a sequence of characters to be typeset. The main difficulties are that such
\r
1420 strings can contain arbitrary eight-bit bytes and there is no fixed limit on
\r
1421 the length of the string that needs to be produced. In extreme cases this
\r
1422 can lead to expressions such as
\r
1424 \hbox{\.{char7\&char15\&char31\&"?FWayzz"}}
\r
1425 \hbox{\.{\&"zzaF"\&char15\&char3\&char31}}
\r
1426 \hbox{\.{\&"Nxzzzzzzzwvtsqo"}}}
\r
1429 @ A global variable |state| keeps track of the output process.
\r
1430 When |state=normal| we have begun a quoted string and the next character
\r
1431 should be a printable character or a closing quote. When |state=special|
\r
1432 the last thing printed was a ``\.{char}'' construction or a closing quote
\r
1433 and an ampersand should come next. The starting condition |state=initial|
\r
1434 is a lot like |state=special|, except no ampersand is required.
\r
1436 @d special 0 /* the |state| after printing a ``\.{char}'' expression */
\r
1437 @d normal 1 /* the |state| value in a quoted string */
\r
1438 @d initial 2 /* initial |state| */
\r
1441 int state; /* controls the process of printing a string */
\r
1442 int print_col; /* there are at most this many characters on the current line */
\r
1444 @ @<Set initial values@>=
\r
1445 mpx->state = initial;
\r
1446 mpx->print_col = 0; /* there are at most this many characters on the current line */
\r
1448 @ To print a string on the \.{MPX} file, initialize |print_col|, ensure that
\r
1449 |state=initial|, and pass the characters one-at-a-time to |print_char|.
\r
1451 @<Declare subroutines for printing strings@>=
\r
1452 static void mpx_print_char (MPX mpx, unsigned char c) {
\r
1453 integer l; /* number of characters to print |c| or the \.{char} expression */
\r
1454 if ( printable(c) ) l=1;
\r
1455 else if ( c<10 ) l=5;
\r
1456 else if ( c<100 ) l=6;
\r
1458 if ( mpx->print_col+l>line_length-2 ) {
\r
1459 if ( mpx->state==normal ) {
\r
1460 fprintf(mpx->mpxfile,"\""); mpx->state=special;
\r
1462 fprintf(mpx->mpxfile,"\n");
\r
1465 @<Print |c| and update |state| and |print_col|@>;
\r
1468 @ @<Print |c| and update |state| and |print_col|@>=
\r
1469 if ( mpx->state==normal ) {
\r
1470 if ( printable(c) ) {
\r
1471 fprintf(mpx->mpxfile,"%c",xchr(c));
\r
1473 fprintf(mpx->mpxfile,"\"&char%d",c);
\r
1474 mpx->print_col +=2;
\r
1477 if ( mpx->state==special ) {
\r
1478 fprintf(mpx->mpxfile,"&");
\r
1479 incr(mpx->print_col);
\r
1481 if ( printable(c) ) {
\r
1482 fprintf(mpx->mpxfile,"\"%c",xchr(c));
\r
1483 incr(mpx->print_col);
\r
1485 fprintf(mpx->mpxfile,"char%d",c);
\r
1488 mpx->print_col += l;
\r
1489 if ( printable(c) )
\r
1490 mpx->state=normal;
\r
1492 mpx->state=special
\r
1494 @ The |end_char_string| procedure gets the string ended properly and ensures
\r
1495 that there is room for |l| more characters on the output line.
\r
1497 @<Declare subroutines for printing strings@>=
\r
1498 static void mpx_end_char_string (MPX mpx,integer l) {
\r
1499 while ( mpx->state>special ){
\r
1500 fprintf(mpx->mpxfile,"\"");
\r
1501 incr(mpx->print_col);
\r
1504 if ( mpx->print_col+l>line_length ) {
\r
1505 fprintf(mpx->mpxfile,"\n "); mpx->print_col=0;
\r
1507 mpx->state=initial; /* get ready to print the next string */
\r
1510 @ Since |end_char_string| resets |state:=initial|, all we have to do is set
\r
1511 |state:=initial| once at the beginning.
\r
1514 mpx->state=initial;
\r
1516 @ Characters and rules are positioned according to global variables |h| and~|v|
\r
1517 as will be explained later. We also need scale factors that convert quantities
\r
1518 to the right units when they are printed in the \.{MPX} file.
\r
1520 Even though all variable names in the \MP\ output are made local via \.{save}
\r
1521 commands, it is still desirable to preceed them with underscores. This makes
\r
1522 the output more likely to work when used in a macro definition, since the
\r
1523 generated variables names must not collide with formal parameters in such
\r
1528 integer v; /* the current position in \.{DVI} units */
\r
1529 double conv; /* converts \.{DVI} units to \MP\ points */
\r
1530 double mag; /* magnification factor times 1000 */
\r
1532 @ @c @<Declare a procedure called |finish_last_char|@>@;
\r
1533 static void mpx_do_set_char (MPX mpx,integer f, integer c) {
\r
1534 if ( (c<mpx->font_bc[f])||(c>mpx->font_ec[f]) )
\r
1535 mpx_abort(mpx,"attempt to typeset invalid character %d",c);
\r
1536 @.attempt to typeset...@>
\r
1537 if ((mpx->h!=mpx->str_h2)||(mpx->v!=mpx->str_v)||
\r
1538 (f!=mpx->str_f)||(mpx->dvi_scale!=mpx->str_scale) ) {
\r
1539 if ( mpx->str_f>=0 ) {
\r
1540 mpx_finish_last_char(mpx);
\r
1541 } else if ( ! mpx->fonts_used ) {
\r
1542 @<Prepare to output the first character on a page@>;
\r
1544 if ( ! mpx->font_used[f] )
\r
1545 @<Prepare to use font |f| for the first time on a page@>;
\r
1546 fprintf(mpx->mpxfile,"_s("); mpx->print_col=3;@/
\r
1547 mpx->str_scale=mpx->dvi_scale; mpx->str_f=f;
\r
1548 mpx->str_v=mpx->v; mpx->str_h1=mpx->h;
\r
1550 mpx_print_char(mpx, c);
\r
1551 mpx->str_h2=mpx->h+@<Width of character |c| in font |f|@>;
\r
1555 boolean font_used[(max_fonts+1)]; /* has this font been used on this page? */
\r
1556 boolean fonts_used; /* has any font been used on this page? */
\r
1557 boolean rules_used; /* has any rules been set on this page? */
\r
1559 integer str_v; /* starting position for current output string */
\r
1560 integer str_h2; /* where the current output string ends */
\r
1561 integer str_f; /* internal font number for the current output string */
\r
1562 double str_scale; /* value of |dvi_scale| for the current output string */
\r
1565 @ Before using any fonts we need to define a MetaPost macro for
\r
1566 typesetting character strings. The |font_used| array is not
\r
1567 initialized until it is actually time to output a character.
\r
1570 static void mpx_prepare_font_use(MPX mpx);
\r
1573 static void mpx_prepare_font_use(MPX mpx) {
\r
1575 for (k=0; k<mpx->nfonts;k++ )
\r
1576 mpx->font_used[k]=false;
\r
1577 mpx->fonts_used=true;
\r
1578 fprintf(mpx->mpxfile,"string _n[];\n");
\r
1579 fprintf(mpx->mpxfile,"vardef _s(expr _t,_f,_m,_x,_y)(text _c)=\n");
\r
1580 fprintf(mpx->mpxfile,
\r
1581 " addto _p also _t infont _f scaled _m shifted (_x,_y) _c; enddef;\n");
\r
1584 @ @<Prepare to output the first character on a page@>=
\r
1585 mpx_prepare_font_use(mpx)
\r
1588 @ @<Do any other initialization required for the new font |f|@>=
\r
1589 mpx->font_used[f]=false;
\r
1591 @ Do what is necessary when the font with internal number f is used for the
\r
1592 first time on a page.
\r
1595 static void mpx_first_use(MPX mpx, int f) ;
\r
1598 static void mpx_first_use(MPX mpx, int f) {
\r
1599 mpx->font_used[f]=true;
\r
1600 fprintf(mpx->mpxfile,"_n%d=",f);
\r
1602 mpx_print_font(mpx, f);
\r
1603 mpx_end_char_string(mpx, 1);
\r
1604 fprintf(mpx->mpxfile,";\n");
\r
1607 @ @<Prepare to use font |f| for the first time on a page@>=
\r
1608 mpx_first_use(mpx,f);
\r
1610 @ We maintain the invariant that |str_f=-1| when there is no output string
\r
1611 under construction.
\r
1613 @<Declare a procedure called |finish_last_char|@>=
\r
1614 static void mpx_finish_last_char (MPX mpx) {
\r
1616 /* font scale factor and \MP\ coordinates of reference point */
\r
1617 if ( mpx->str_f>=0 ) {
\r
1618 if (mpx->mode==mpx_tex_mode) {
\r
1619 m=mpx->str_scale*mpx->font_scaled_size[mpx->str_f]*
\r
1620 mpx->mag/mpx->font_design_size[mpx->str_f];
\r
1621 x=mpx->conv*mpx->str_h1;
\r
1622 y=mpx->conv*(-mpx->str_v);
\r
1623 if ( (fabs(x)>=4096.0)||(fabs(y)>=4096.0)||(m>=4096.0)||(m<0) ) {
\r
1624 mpx_warn(mpx,"text is out of range");
\r
1625 mpx_end_char_string(mpx, 60);
\r
1627 mpx_end_char_string(mpx, 40);
\r
1629 fprintf(mpx->mpxfile,",_n%d,%1.5f,%1.4f,%1.4f,",mpx->str_f,m,x,y);
\r
1630 @<Print a \.{withcolor} specifier if appropriate@>@/
\r
1631 fprintf(mpx->mpxfile,");\n");
\r
1633 m = mpx->str_size / mpx->font_design_size[mpx->str_f];
\r
1634 x = mpx->dmp_str_h1 * mpx->unit;
\r
1635 y = YCORR - mpx->dmp_str_v * mpx->unit;
\r
1636 if (fabs(x) >= 4096.0 || fabs(y) >= 4096.0 || m >= 4096.0 || m < 0) {
\r
1637 mpx_warn(mpx,"text out of range ignored");
\r
1638 mpx_end_char_string(mpx,67);
\r
1640 mpx_end_char_string(mpx,47);
\r
1642 fprintf(mpx->mpxfile, ")infont n%d", mpx->font_num[mpx->str_f]);
\r
1643 mpx_slant_and_ht(mpx);
\r
1644 fprintf(mpx->mpxfile, ",%.5f,%.4f,%.4f);\n", (m*1.00375), x, y);
\r
1650 @ Setting rules is fairly simple.
\r
1653 static void mpx_do_set_rule (MPX mpx,integer ht, integer wd) {
\r
1654 double xx1,yy1,xx2,yy2,ww;
\r
1655 /* \MP\ coordinates of lower-left and upper-right corners */
\r
1657 @<Handle a special rule that determines the box size@>
\r
1658 } else if ( (ht>0)||(wd>0) ) {
\r
1659 if ( mpx->str_f>=0 )
\r
1660 mpx_finish_last_char(mpx);
\r
1661 if ( ! mpx->rules_used ) {
\r
1662 mpx->rules_used=true;
\r
1663 fprintf(mpx->mpxfile,
\r
1664 "interim linecap:=0;\n"
\r
1665 "vardef _r(expr _a,_w)(text _t) =\n"
\r
1666 " addto _p doublepath _a withpen pencircle scaled _w _t enddef;");
\r
1668 @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke
\r
1669 and |ww| the desired stroke width@>;
\r
1670 if ( (fabs(xx1)>=4096.0)||(fabs(yy1)>=4096.0)||@|
\r
1671 (fabs(xx2)>=4096.0)||(fabs(yy2)>=4096.0)||(ww>=4096.0) )
\r
1672 mpx_warn(mpx,"hrule or vrule is out of range");
\r
1673 fprintf(mpx->mpxfile,"_r((%1.4f,%1.4f)..(%1.4f,%1.4f), %1.4f,",xx1,yy1,xx2,yy2,ww);
\r
1674 @<Print a \.{withcolor} specifier if appropriate@>@/
\r
1675 fprintf(mpx->mpxfile,");\n");
\r
1679 @ @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke...@>=
\r
1680 xx1=mpx->conv*mpx->h;
\r
1681 yy1=mpx->conv*(-mpx->v);
\r
1683 xx2=xx1+mpx->conv*wd;
\r
1688 yy2=yy1+mpx->conv*ht;
\r
1694 @ Rules of width one dvi unit are not typeset since \.{MPtoTeX} adds an
\r
1695 extraneous rule of this width in order to allow \.{DVItoMP} to deduce the
\r
1696 dimensions of the boxes it ships out. The box width is the left edge of the
\r
1697 last such rule; the height and depth are at the top and bottom of the rule.
\r
1698 There should be only one special rule per picture but there could be more if
\r
1699 the user tries to typeset his own one-dvi-unit rules. In this case the
\r
1700 dimension-determining rule is the last one in the picture.
\r
1702 @<Handle a special rule that determines the box size@>=
\r
1704 mpx->pic_wd=mpx->h; mpx->pic_dp=mpx->v; mpx->pic_ht=ht-mpx->v;
\r
1708 integer pic_dp; integer pic_ht; integer pic_wd; /* picture dimensions from special rule */
\r
1710 @ The following initialization and clean-up is required. We do a little more
\r
1711 initialization than is absolutely necessary since some compilers might complain
\r
1712 if the variables are uninitialized when |do_set_char| tests them.
\r
1715 static void mpx_start_picture (MPX mpx) {
\r
1716 mpx->fonts_used=false;
\r
1717 mpx->rules_used=false;
\r
1718 mpx->graphics_used=false;
\r
1722 mpx->str_scale=1.0; /* values don't matter */
\r
1723 mpx->dmp_str_v = 0.0;
\r
1724 mpx->dmp_str_h2 = 0.0;
\r
1725 mpx->str_size = 0.0;
\r
1726 fprintf(mpx->mpxfile,
\r
1727 "begingroup save %s_p,_r,_s,_n; picture _p; _p=nullpicture;\n",
\r
1728 (mpx->mode == mpx_tex_mode ? "" : "_C,_D,"));
\r
1731 static void mpx_stop_picture (MPX mpx) {
\r
1732 double w,h,dd; /* width, height, negative depth in PostScript points */
\r
1733 if ( mpx->str_f>=0 )
\r
1734 mpx_finish_last_char(mpx);
\r
1735 if (mpx->mode==mpx_tex_mode) {
\r
1736 @<Print a \&{setbounds} command based on picture dimensions@>;
\r
1738 fprintf(mpx->mpxfile,"_p endgroup\n");
\r
1741 @ @<Print a \&{setbounds} command based on picture dimensions@>=
\r
1742 dd=-mpx->pic_dp*mpx->conv;
\r
1743 w=mpx->conv*mpx->pic_wd;
\r
1744 h=mpx->conv*mpx->pic_ht;
\r
1745 fprintf(mpx->mpxfile,
\r
1746 "setbounds _p to (0,%1.4f)--(%1.4f,%1.4f)--\n"
\r
1747 " (%1.4f,%1.4f)--(0,%1.4f)--cycle;\n",dd,w,dd,w,h,h)
\r
1749 @* Translation to symbolic form.
\r
1751 The main work of \.{DVItoMP} is accomplished by the |do_dvi_commands|
\r
1752 procedure, which produces the output for an entire page, assuming that the
\r
1753 |bop| command for that page has already been processed. This procedure is
\r
1754 essentially an interpretive routine that reads and acts on the \.{DVI}
\r
1755 commands. It is also capable of executing the typesetting commands for
\r
1756 a character in a virtual font.
\r
1758 @ The definition of \.{DVI} files refers to six registers,
\r
1759 $(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units.
\r
1760 These units come directly from the input file except they need to be
\r
1761 rescaled when typesetting characters from a virtual font.
\r
1762 The stack of $(h,v,w,x,y,z)$ values is represented by six arrays
\r
1763 called |hstack|, \dots, |zstack|.
\r
1766 integer w;integer x;integer y;integer z;
\r
1767 /* current state values (|h| and |v| have already been declared) */
\r
1768 integer hstack[(stack_size+1)];
\r
1769 integer vstack[(stack_size+1)];
\r
1770 integer wstack[(stack_size+1)];
\r
1771 integer xstack[(stack_size+1)];
\r
1772 integer ystack[(stack_size+1)];
\r
1773 integer zstack[(stack_size+1)]; /* pushed down values in \.{DVI} units */
\r
1774 integer stk_siz; /* the current stack size */
\r
1775 double dvi_scale; /* converts units of current input source to \.{DVI} units */
\r
1777 @ @<Do initialization required before starting a new page@>=
\r
1778 mpx->dvi_scale=1.0;
\r
1780 mpx->h=0; mpx->v=0;
\r
1781 mpx->Xslant = 0.0; mpx->Xheight = 0.0
\r
1783 @ Next, we need procedures to handle |push| and |pop| commands.
\r
1785 @c @<Declare procedures to handle color commands@>
\r
1786 static void mpx_do_push (MPX mpx) {
\r
1787 if ( mpx->stk_siz==stack_size )
\r
1788 mpx_abort(mpx,"DVItoMP capacity exceeded (stack size=%d)",stack_size);
\r
1789 @.DVItoMP capacity exceeded...@>
\r
1790 mpx->hstack[mpx->stk_siz]=mpx->h;
\r
1791 mpx->vstack[mpx->stk_siz]=mpx->v; mpx->wstack[mpx->stk_siz]=mpx->w;
\r
1792 mpx->xstack[mpx->stk_siz]=mpx->x;
\r
1793 mpx->ystack[mpx->stk_siz]=mpx->y; mpx->zstack[mpx->stk_siz]=mpx->z;
\r
1794 incr(mpx->stk_siz);
\r
1797 static void mpx_do_pop (MPX mpx) {
\r
1798 if ( mpx->stk_siz==0 )
\r
1799 bad_dvi("attempt to pop empty stack");
\r
1801 decr(mpx->stk_siz);
\r
1802 mpx->h=mpx->hstack[mpx->stk_siz];
\r
1803 mpx->v=mpx->vstack[mpx->stk_siz]; mpx->w=mpx->wstack[mpx->stk_siz];
\r
1804 mpx->x=mpx->xstack[mpx->stk_siz];
\r
1805 mpx->y=mpx->ystack[mpx->stk_siz]; mpx->z=mpx->zstack[mpx->stk_siz];
\r
1809 @ The |set_virtual_char| procedure is mutually recursive with
\r
1810 |do_dvi_commands|. This is really a supervisory
\r
1812 procedure that calls |do_set_char| or adjusts the input source to read
\r
1813 typesetting commands for a character in a virtual font.
\r
1816 static void mpx_do_dvi_commands (MPX mpx);
\r
1817 static void mpx_set_virtual_char (MPX mpx,integer f, integer c) {
\r
1818 double old_scale; /* original value of |dvi_scale| */
\r
1819 int old_buf_ptr; /* original value of the input pointer |buf_ptr| */
\r
1820 int old_fbase,old_ftop; /* originally applicable part of the |font_num| table */
\r
1821 if ( mpx->fbase[f]==0 )
\r
1822 mpx_do_set_char(mpx, f,c);
\r
1824 old_fbase=mpx->cur_fbase; old_ftop=mpx->cur_ftop;
\r
1825 mpx->cur_fbase=mpx->fbase[f];
\r
1826 mpx->cur_ftop=mpx->ftop[f];
\r
1827 old_scale=mpx->dvi_scale;
\r
1828 mpx->dvi_scale=mpx->dvi_scale*mpx->font_scaled_size[f];
\r
1829 old_buf_ptr=mpx->buf_ptr;
\r
1830 mpx->buf_ptr=start_cmd(f,c);
\r
1832 mpx_do_dvi_commands(mpx);
\r
1833 mpx_do_pop(mpx);@/
\r
1834 mpx->buf_ptr=old_buf_ptr;
\r
1835 mpx->dvi_scale=old_scale;
\r
1836 mpx->cur_fbase=old_fbase;
\r
1837 mpx->cur_ftop=old_ftop;
\r
1841 @ Before we get into the details of |do_dvi_commands|, it is convenient to
\r
1842 consider a simpler routine that computes the first parameter of each
\r
1845 @d four_cases(A) (A): case (A)+1: case (A)+2: case (A)+3
\r
1846 @d eight_cases(A) four_cases((A)): case four_cases((A)+4)
\r
1847 @d sixteen_cases(A) eight_cases((A)): case eight_cases((A)+8)
\r
1848 @d thirty_two_cases(A) sixteen_cases((A)): case sixteen_cases((A)+16)
\r
1849 @d sixty_four_cases(A) thirty_two_cases((A)): case thirty_two_cases((A)+32)
\r
1851 @<Declare a function called |first_par|@>=
\r
1852 static integer mpx_first_par (MPX mpx, unsigned int o) {
\r
1854 case sixty_four_cases(set_char_0):
\r
1855 case sixty_four_cases(set_char_0+64):
\r
1856 return (o-set_char_0);
\r
1858 case set1: case put1: case fnt1: case xxx1: case fnt_def1:
\r
1859 return mpx_get_byte(mpx);
\r
1861 case set1+1: case put1+1: case fnt1+1: case xxx1+1: case fnt_def1+1:
\r
1862 return mpx_get_two_bytes(mpx);
\r
1864 case set1+2: case put1+2: case fnt1+2: case xxx1+2: case fnt_def1+2:
\r
1865 return mpx_get_three_bytes(mpx);
\r
1867 case right1: case w1: case x1: case down1: case y1: case z1:
\r
1868 return mpx_signed_byte(mpx);
\r
1870 case right1+1: case w1+1: case x1+1: case down1+1: case y1+1: case z1+1:
\r
1871 return mpx_signed_pair(mpx);
\r
1873 case right1+2: case w1+2: case x1+2: case down1+2: case y1+2: case z1+2:
\r
1874 return mpx_signed_trio(mpx);
\r
1876 case set1+3: case set_rule: case put1+3: case put_rule:
\r
1877 case right1+3: case w1+3: case x1+3: case down1+3: case y1+3: case z1+3:
\r
1878 case fnt1+3: case xxx1+3: case fnt_def1+3:
\r
1879 return mpx_signed_quad(mpx);
\r
1881 case nop: case bop: case eop: case push: case pop: case pre: case post:
\r
1882 case post_post: case undefined_commands:
\r
1885 case w0: return mpx->w; break;
\r
1886 case x0: return mpx->x; break;
\r
1887 case y0: return mpx->y; break;
\r
1888 case z0: return mpx->z; break;
\r
1889 case sixty_four_cases(fnt_num_0):
\r
1890 return (o-fnt_num_0);
\r
1893 return 0; /* compiler warning */
\r
1896 @ Here is the |do_dvi_commands| procedure.
\r
1899 static void mpx_do_dvi_commands (MPX mpx) {
\r
1900 unsigned int o; /* operation code of the current command */
\r
1901 integer p,q; /* parameters of the current command */
\r
1902 integer cur_font; /* current internal font number */
\r
1903 if ( (mpx->cur_fbase<mpx->cur_ftop) && (mpx->buf_ptr<virtual_space) )
\r
1904 cur_font=mpx_select_font(mpx, mpx->font_num[mpx->cur_ftop-1]); /* select first local font */
\r
1906 cur_font=max_fnums+1; /* current font is undefined */
\r
1907 mpx->w=0; mpx->x=0; mpx->y=0; mpx->z=0; /* initialize the state variables */
\r
1909 @<Translate the next command in the \.{DVI} file; |return| if it was |eop|@>;
\r
1913 @ The multiway switch in |first_par|, above, was organized by the length
\r
1914 of each command; the one in |do_dvi_commands| is organized by the semantics.
\r
1916 @ @<Translate the next command...@>=
\r
1918 o=mpx_get_byte(mpx);
\r
1919 p=mpx_first_par(mpx, o);
\r
1920 if ( feof(mpx->dvi_file) )
\r
1921 bad_dvi("the DVI file ended prematurely");
\r
1922 @.the DVI file ended prematurely@>
\r
1923 if ( o<set1+4 ) { /* |set_char_0| through |set_char_127|, |set1| through |set4| */
\r
1924 if ( cur_font>max_fnums ) {
\r
1925 if ( mpx->vf_reading )
\r
1926 mpx_abort(mpx,"no font selected for character %d in virtual font", p);
\r
1928 bad_dvi_two("no font selected for character %d",p);
\r
1930 @.no font selected@>
\r
1931 mpx_set_virtual_char(mpx, cur_font,p);
\r
1932 mpx->h += @<Width of character |p| in font |cur_font|@>;
\r
1935 case four_cases(put1):
\r
1936 mpx_set_virtual_char(mpx, cur_font, p);
\r
1939 q=trunc(mpx_signed_quad(mpx)*mpx->dvi_scale);
\r
1940 mpx_do_set_rule(mpx, trunc(p*mpx->dvi_scale),q);
\r
1944 q=trunc(mpx_signed_quad(mpx)*mpx->dvi_scale);
\r
1945 mpx_do_set_rule(mpx, trunc(p*mpx->dvi_scale),q);
\r
1947 @<Additional cases for translating \.{DVI} command |o| with
\r
1948 first parameter |p|@>@;
\r
1949 case undefined_commands:
\r
1950 bad_dvi_two("undefined command %d",o);
\r
1951 @.undefined command@>
\r
1953 } /* all cases have been enumerated */
\r
1957 @ @<Additional cases for translating \.{DVI} command |o|...@>=
\r
1958 case four_cases(xxx1):
\r
1959 mpx_do_xxx(mpx, p);
\r
1961 case pre: case post: case post_post:
\r
1962 bad_dvi("preamble or postamble within a page!");
\r
1963 @.preamble or postamble within a page@>
\r
1966 @ @<Additional cases for translating \.{DVI} command |o|...@>=
\r
1970 bad_dvi("bop occurred before eop");
\r
1971 @.bop occurred before eop@>
\r
1974 if ( mpx->stk_siz!=0 )
\r
1975 bad_dvi("stack not empty at end of page");
\r
1976 @.stack not empty...@>
\r
1986 @ @<Additional cases for translating \.{DVI} command |o|...@>=
\r
1987 case four_cases(right1):
\r
1988 mpx->h += trunc(p*mpx->dvi_scale);
\r
1990 case w0: case four_cases(w1):
\r
1991 mpx->w = trunc(p*mpx->dvi_scale); mpx->h += mpx->w;
\r
1993 case x0: case four_cases(x1):
\r
1994 mpx->x = trunc(p*mpx->dvi_scale); mpx->h += mpx->x;
\r
1996 case four_cases(down1):
\r
1997 mpx->v += trunc(p*mpx->dvi_scale);
\r
1999 case y0: case four_cases(y1):
\r
2000 mpx->y = trunc(p*mpx->dvi_scale); mpx->v += mpx->y;
\r
2002 case z0: case four_cases(z1):
\r
2003 mpx->z = trunc(p*mpx->dvi_scale); mpx->v += mpx->z;
\r
2006 @ @<Additional cases for translating \.{DVI} command |o|...@>=
\r
2007 case sixty_four_cases(fnt_num_0): case four_cases(fnt1):
\r
2008 cur_font = mpx_select_font(mpx, p);
\r
2010 case four_cases(fnt_def1):
\r
2011 mpx_define_font(mpx, p);
\r
2014 @* The main program.
\r
2015 Now we are ready to put it all together. This is where \.{DVItoMP} starts,
\r
2016 and where it ends.
\r
2019 static int mpx_dvitomp (MPX mpx, char *dviname) {
\r
2021 mpx->dviname = dviname;
\r
2022 mpx_open_dvi_file(mpx);
\r
2023 @<Process the preamble@>;
\r
2024 mpx_open_mpxfile(mpx);
\r
2025 fprintf (mpx->mpxfile,mpx->banner);
\r
2026 fprintf (mpx->mpxfile,"\n");
\r
2028 @<Advance to the next |bop| command@>;
\r
2029 for (k=0;k<=10;k++)
\r
2030 (void)mpx_signed_quad(mpx);
\r
2031 @<Do initialization required before starting a new page@>;
\r
2032 mpx_start_picture(mpx);
\r
2033 mpx_do_dvi_commands(mpx);
\r
2034 mpx_stop_picture(mpx);
\r
2035 fprintf(mpx->mpxfile,"mpxbreak\n");
\r
2037 if ( mpx->history<=mpx_cksum_trouble )
\r
2040 return mpx->history;
\r
2043 @ The main program needs a few global variables in order to do its work.
\r
2046 integer k;integer p; /* general purpose registers */
\r
2047 integer numerator;integer denominator; /* stated conversion ratio */
\r
2049 @ @<Process the preamble@>=
\r
2052 p=mpx_get_byte(mpx); /* fetch the first byte */
\r
2054 bad_dvi("First byte isn""t start of preamble!");
\r
2055 @.First byte isn't...@>
\r
2056 p=mpx_get_byte(mpx); /* fetch the identification byte */
\r
2058 mpx_warn(mpx,"identification in byte 1 should be %d!", id_byte);
\r
2059 @.identification...should be n@>
\r
2060 @<Compute the conversion factor@>;
\r
2061 p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */
\r
2064 (void)mpx_get_byte(mpx);
\r
2068 @ The conversion factor |conv| is figured as follows: There are exactly
\r
2069 |n/d| decimicrons per \.{DVI} unit, and 254000 decimicrons per inch,
\r
2070 and |resolution| pixels per inch. Then we have to adjust this
\r
2071 by the stated amount of magnification. No such adjustment is needed for
\r
2072 |dvi_per_fix| since it is used to convert design sizes.
\r
2074 @<Compute the conversion factor@>=
\r
2075 mpx->numerator=mpx_signed_quad(mpx); mpx->denominator=mpx_signed_quad(mpx);
\r
2076 if ( (mpx->numerator<=0)||(mpx->denominator<=0) )
\r
2077 bad_dvi("bad scale ratio in preamble");
\r
2078 @.bad scale ratio@>
\r
2079 mpx->mag=mpx_signed_quad(mpx)/1000.0;
\r
2080 if ( mpx->mag<=0.0 )
\r
2081 bad_dvi("magnification isn't positive");
\r
2082 @.magnification isn't positive@>
\r
2083 mpx->conv=(mpx->numerator/254000.0)*(72.0/mpx->denominator)*mpx->mag;
\r
2084 mpx->dvi_per_fix=(254000.0/mpx->numerator)*(mpx->denominator/72.27)/1048576.0;
\r
2086 @ @<Advance to the next |bop| command@>=
\r
2089 k=mpx_get_byte(mpx);
\r
2090 if ( (k>=fnt_def1)&&(k<fnt_def1+4) ){
\r
2091 p=mpx_first_par(mpx, k);
\r
2092 mpx_define_font(mpx, p); k=nop;
\r
2098 bad_dvi("missing bop");
\r
2102 @ Global filenames.
\r
2108 These changes support \.{dvips}-style ``\.{color push NAME}'' and
\r
2109 ``\.{color pop}'' specials. We store a list of named colors, sorted by
\r
2110 name, and decorate the relevant drawing commands with ``\.{withcolor
\r
2111 (r,g,b)}'' specifiers while a color is defined.
\r
2113 @ A constant bounding the size of the named-color array.
\r
2115 @d max_named_colors 100 /* maximum number of distinct named colors */
\r
2117 @ Then we declare a record for color types.
\r
2120 typedef struct named_color_record {
\r
2121 char *name; /* color name */
\r
2122 char *value; /* text to pass to MetaPost */
\r
2123 } named_color_record;
\r
2125 @ Declare the named-color array itself.
\r
2128 named_color_record named_colors[(max_named_colors+1)];
\r
2129 /* stores information about named colors, in sorted order by name */
\r
2130 integer num_named_colors; /* number of elements of |named_colors| that are valid */
\r
2132 @ This function, used only during initialization, defines a named color.
\r
2135 static void mpx_def_named_color (MPX mpx, char *n, char *v) {
\r
2136 mpx->num_named_colors++;
\r
2137 assert(mpx->num_named_colors<max_named_colors);
\r
2138 mpx->named_colors[mpx->num_named_colors].name = n;
\r
2139 mpx->named_colors[mpx->num_named_colors].value = v;
\r
2142 @ @<Declarations@>=
\r
2143 static void mpx_def_named_color (MPX mpx, char *n, char *v);
\r
2145 @ During the initialization phase, we define values for all the named
\r
2146 colors defined in \.{colordvi.tex}. CMYK-to-RGB conversion by GhostScript.
\r
2148 This list has to be sorted alphabetically!
\r
2150 @<Set initial values@>=
\r
2151 mpx->num_named_colors = 0;
\r
2152 mpx_def_named_color(mpx, "Apricot", "(1.0, 0.680006, 0.480006)");
\r
2153 mpx_def_named_color(mpx, "Aquamarine", "(0.180006, 1.0, 0.7)");
\r
2154 mpx_def_named_color(mpx, "Bittersweet", "(0.760012, 0.0100122, 0.0)");
\r
2155 mpx_def_named_color(mpx, "Black", "(0.0, 0.0, 0.0)");
\r
2156 mpx_def_named_color(mpx, "Blue", "(0.0, 0.0, 1.0)");
\r
2157 mpx_def_named_color(mpx, "BlueGreen", "(0.15, 1.0, 0.669994)");
\r
2158 mpx_def_named_color(mpx, "BlueViolet", "(0.1, 0.05, 0.960012)");
\r
2159 mpx_def_named_color(mpx, "BrickRed", "(0.719994, 0.0, 0.0)");
\r
2160 mpx_def_named_color(mpx, "Brown", "(0.4, 0.0, 0.0)");
\r
2161 mpx_def_named_color(mpx, "BurntOrange", "(1.0, 0.489988, 0.0)");
\r
2162 mpx_def_named_color(mpx, "CadetBlue", "(0.380006, 0.430006, 0.769994)");
\r
2163 mpx_def_named_color(mpx, "CarnationPink", "(1.0, 0.369994, 1.0)");
\r
2164 mpx_def_named_color(mpx, "Cerulean", "(0.0600122, 0.889988, 1.0)");
\r
2165 mpx_def_named_color(mpx, "CornflowerBlue", "(0.35, 0.869994, 1.0)");
\r
2166 mpx_def_named_color(mpx, "Cyan", "(0.0, 1.0, 1.0)");
\r
2167 mpx_def_named_color(mpx, "Dandelion", "(1.0, 0.710012, 0.160012)");
\r
2168 mpx_def_named_color(mpx, "DarkOrchid", "(0.6, 0.2, 0.8)");
\r
2169 mpx_def_named_color(mpx, "Emerald", "(0.0, 1.0, 0.5)");
\r
2170 mpx_def_named_color(mpx, "ForestGreen", "(0.0, 0.880006, 0.0)");
\r
2171 mpx_def_named_color(mpx, "Fuchsia", "(0.45, 0.00998169, 0.919994)");
\r
2172 mpx_def_named_color(mpx, "Goldenrod", "(1.0, 0.9, 0.160012)");
\r
2173 mpx_def_named_color(mpx, "Gray", "(0.5, 0.5, 0.5)");
\r
2174 mpx_def_named_color(mpx, "Green", "(0.0, 1.0, 0.0)");
\r
2175 mpx_def_named_color(mpx, "GreenYellow", "(0.85, 1.0, 0.310012)");
\r
2176 mpx_def_named_color(mpx, "JungleGreen", "(0.0100122, 1.0, 0.480006)");
\r
2177 mpx_def_named_color(mpx, "Lavender", "(1.0, 0.519994, 1.0)");
\r
2178 mpx_def_named_color(mpx, "LimeGreen", "(0.5, 1.0, 0.0)");
\r
2179 mpx_def_named_color(mpx, "Magenta", "(1.0, 0.0, 1.0)");
\r
2180 mpx_def_named_color(mpx, "Mahogany", "(0.65, 0.0, 0.0)");
\r
2181 mpx_def_named_color(mpx, "Maroon", "(0.680006, 0.0, 0.0)");
\r
2182 mpx_def_named_color(mpx, "Melon", "(1.0, 0.539988, 0.5)");
\r
2183 mpx_def_named_color(mpx, "MidnightBlue", "(0.0, 0.439988, 0.569994)");
\r
2184 mpx_def_named_color(mpx, "Mulberry", "(0.640018, 0.0800061, 0.980006)");
\r
2185 mpx_def_named_color(mpx, "NavyBlue", "(0.0600122, 0.460012, 1.0)");
\r
2186 mpx_def_named_color(mpx, "OliveGreen", "(0.0, 0.6, 0.0)");
\r
2187 mpx_def_named_color(mpx, "Orange", "(1.0, 0.389988, 0.130006)");
\r
2188 mpx_def_named_color(mpx, "OrangeRed", "(1.0, 0.0, 0.5)");
\r
2189 mpx_def_named_color(mpx, "Orchid", "(0.680006, 0.360012, 1.0)");
\r
2190 mpx_def_named_color(mpx, "Peach", "(1.0, 0.5, 0.3)");
\r
2191 mpx_def_named_color(mpx, "Periwinkle", "(0.430006, 0.45, 1.0)");
\r
2192 mpx_def_named_color(mpx, "PineGreen", "(0.0, 0.75, 0.160012)");
\r
2193 mpx_def_named_color(mpx, "Plum", "(0.5, 0.0, 1.0)");
\r
2194 mpx_def_named_color(mpx, "ProcessBlue", "(0.0399878, 1.0, 1.0)");
\r
2195 mpx_def_named_color(mpx, "Purple", "(0.55, 0.139988, 1.0)");
\r
2196 mpx_def_named_color(mpx, "RawSienna", "(0.55, 0.0, 0.0)");
\r
2197 mpx_def_named_color(mpx, "Red", "(1.0, 0.0, 0.0)");
\r
2198 mpx_def_named_color(mpx, "RedOrange", "(1.0, 0.230006, 0.130006)");
\r
2199 mpx_def_named_color(mpx, "RedViolet", "(0.590018, 0.0, 0.660012)");
\r
2200 mpx_def_named_color(mpx, "Rhodamine", "(1.0, 0.180006, 1.0)");
\r
2201 mpx_def_named_color(mpx, "RoyalBlue", "(0.0, 0.5, 1.0)");
\r
2202 mpx_def_named_color(mpx, "RoyalPurple", "(0.25, 0.1, 1.0)");
\r
2203 mpx_def_named_color(mpx, "RubineRed", "(1.0, 0.0, 0.869994)");
\r
2204 mpx_def_named_color(mpx, "Salmon", "(1.0, 0.469994, 0.619994)");
\r
2205 mpx_def_named_color(mpx, "SeaGreen", "(0.310012, 1.0, 0.5)");
\r
2206 mpx_def_named_color(mpx, "Sepia", "(0.3, 0.0, 0.0)");
\r
2207 mpx_def_named_color(mpx, "SkyBlue", "(0.380006, 1.0, 0.880006)");
\r
2208 mpx_def_named_color(mpx, "SpringGreen", "(0.739988, 1.0, 0.239988)");
\r
2209 mpx_def_named_color(mpx, "Tan", "(0.860012, 0.580006, 0.439988)");
\r
2210 mpx_def_named_color(mpx, "TealBlue", "(0.119994, 0.980006, 0.640018)");
\r
2211 mpx_def_named_color(mpx, "Thistle", "(0.880006, 0.410012, 1.0)");
\r
2212 mpx_def_named_color(mpx, "Turquoise", "(0.15, 1.0, 0.8)");
\r
2213 mpx_def_named_color(mpx, "Violet", "(0.210012, 0.119994, 1.0)");
\r
2214 mpx_def_named_color(mpx, "VioletRed", "(1.0, 0.189988, 1.0)");
\r
2215 mpx_def_named_color(mpx, "White", "(1.0, 1.0, 1.0)");
\r
2216 mpx_def_named_color(mpx, "WildStrawberry", "(1.0, 0.0399878, 0.610012)");
\r
2217 mpx_def_named_color(mpx, "Yellow", "(1.0, 1.0, 0.0)");
\r
2218 mpx_def_named_color(mpx, "YellowGreen", "(0.560012, 1.0, 0.260012)");
\r
2219 mpx_def_named_color(mpx, "YellowOrange", "(1.0, 0.580006, 0.0)");
\r
2221 @ Color commands get a separate warning procedure. |warn| sets |history :=
\r
2222 mpx_warning_given|, which causes a nonzero exit status; but color errors are
\r
2223 trivial and should leave the exit status zero.
\r
2225 @d color_warn(A) mpx_warn(mpx,A)
\r
2226 @d color_warn_two(A,B) mpx_warn(mpx,"%s%s",A,B)
\r
2228 @ The |do_xxx| procedure handles DVI specials (defined with the |xxx1...xxx4| commands).
\r
2232 @<Declare procedures to handle color commands@>=
\r
2233 static void mpx_do_xxx (MPX mpx, integer p)
\r
2235 unsigned char buf[(XXX_BUF+1)]; /* FIXME: Fixed size buffer. */
\r
2236 integer l, r, m, k, len;
\r
2238 int bufsiz = XXX_BUF;
\r
2240 while ( ( p > 0) && (len < bufsiz) ) {
\r
2241 buf[len] = mpx_get_byte(mpx);
\r
2242 decr(p); incr(len);
\r
2244 @<Check whether |buf| contains a color command; if not, |goto XXXX|@>
\r
2246 color_warn("long \"color\" special ignored");
\r
2249 if ( @<|buf| contains a color pop command@> ) {
\r
2250 @<Handle a color pop command@>
\r
2251 } else if ( @<|buf| contains a color push command@> ) {
\r
2252 @<Handle a color push command@>
\r
2254 color_warn("unknown \"color\" special ignored");
\r
2258 for (k = 1;k<=p;k++) (void)mpx_get_byte(mpx);
\r
2263 @<Check whether |buf| contains a color command; if not, |goto XXXX|@>=
\r
2265 || (buf[0] != 'c')
\r
2266 || (buf[1] != 'o')
\r
2267 || (buf[2] != 'l')
\r
2268 || (buf[3] != 'o')
\r
2269 || (buf[4] != 'r')
\r
2270 || (buf[5] != ' ')
\r
2273 @ @<|buf| contains a color push command@>=
\r
2275 (buf[6] == 'p') &&
\r
2276 (buf[7] == 'u') &&
\r
2277 (buf[8] == 's') &&
\r
2278 (buf[9] == 'h') &&
\r
2281 @ @<|buf| contains a color pop command@>=
\r
2283 (buf[6] == 'p') &&
\r
2284 (buf[7] == 'o') &&
\r
2287 @ The \.{color push} and \.{pop} commands imply a color stack, so we need a
\r
2288 global variable to hold that stack.
\r
2290 @d max_color_stack_depth 10 /* maximum depth of saved color stack */
\r
2292 @ Here's the actual stack variables.
\r
2295 integer color_stack_depth; /* current depth of saved color stack */
\r
2296 char *color_stack[(max_color_stack_depth+1)]; /* saved color stack */
\r
2298 @ Initialize the stack to empty.
\r
2300 @<Set initial values@>=
\r
2301 mpx->color_stack_depth = 0;
\r
2303 @ \.{color pop} just pops the stack.
\r
2305 @<Handle a color pop command@>=
\r
2306 mpx_finish_last_char(mpx);
\r
2307 if (mpx->color_stack_depth > 0 ) {
\r
2308 free(mpx->color_stack[mpx->color_stack_depth]);
\r
2309 decr(mpx->color_stack_depth);
\r
2311 color_warn("color stack underflow");
\r
2314 @ \.{color push} pushes a color onto the stack.
\r
2316 @<Handle a color push command@>=
\r
2317 mpx_finish_last_char(mpx);
\r
2318 if ( mpx->color_stack_depth >= max_color_stack_depth )
\r
2319 mpx_abort(mpx,"color stack overflow");
\r
2320 incr(mpx->color_stack_depth);
\r
2321 /* I don't know how to do string operations in Pascal. */
\r
2322 /* Skip over extra spaces after 'color push'. */
\r
2324 while ( (l < len - 1) && (buf[l] == ' ') ) incr(l);
\r
2325 if ( @<|buf[l]| contains an rgb command@> ) {
\r
2326 @<Handle a color push rgb command@>
\r
2327 } else if ( @<|buf[l]| contains a cmyk command@> ) {
\r
2328 @<Handle a color push cmyk command@>
\r
2329 } else if ( @<|buf[l]| contains a gray command@> ) {
\r
2330 @<Handle a color push gray command@>
\r
2332 @<Handle a named color push command@>
\r
2335 @ @<|buf[l]| contains an rgb command@>=
\r
2337 && (buf[l] == 'r')
\r
2338 && (buf[l+1] == 'g')
\r
2339 && (buf[l+2] == 'b')
\r
2340 && (buf[l+3] == ' ')
\r
2342 @ @<Handle a color push rgb command@>=
\r
2344 while ( (l < len) && (buf[l] == ' ') ) incr(l); /* Remove spaces at end of buf */
\r
2345 while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
\r
2346 mpx->color_stack[mpx->color_stack_depth]=xmalloc(len-l+3,1);
\r
2348 @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
\r
2350 @ @<|buf[l]| contains a gray command@>=
\r
2352 && (buf[l] == 'g')
\r
2353 && (buf[l+1] == 'r')
\r
2354 && (buf[l+2] == 'a')
\r
2355 && (buf[l+3] == 'y')
\r
2356 && (buf[l+4] == ' ')
\r
2358 @ @<Handle a color push gray command@>=
\r
2360 while ( (l < len) && (buf[l] == ' ') ) incr(l); /* Remove spaces at end of buf */
\r
2361 while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
\r
2362 mpx->color_stack[mpx->color_stack_depth]=xmalloc(len-l+9,1);
\r
2363 strcpy(mpx->color_stack[mpx->color_stack_depth],"white*");
\r
2365 @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
\r
2367 @ @<|buf[l]| contains a cmyk command@>=
\r
2369 && (buf[l] == 'c')
\r
2370 && (buf[l+1] == 'm')
\r
2371 && (buf[l+2] == 'y')
\r
2372 && (buf[l+3] == 'k')
\r
2373 && (buf[l+4] == ' ')
\r
2375 @ @<Handle a color push cmyk command@>=
\r
2377 while ( (l < len) && (buf[l] == ' ') ) incr(l);
\r
2378 /* Remove spaces at end of buf */
\r
2379 while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
\r
2380 mpx->color_stack[mpx->color_stack_depth]=xmalloc(len-l+7,1);
\r
2381 strcpy(mpx->color_stack[mpx->color_stack_depth],"cmyk");
\r
2383 @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
\r
2385 @ @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>=
\r
2386 mpx->color_stack[mpx->color_stack_depth][k] = '(';
\r
2388 while ( l < len ) {
\r
2389 if ( buf[l] == ' ' ) {
\r
2390 mpx->color_stack[mpx->color_stack_depth][k] = ',';
\r
2391 while ( (l < len) && (buf[l] == ' ') ) incr(l);
\r
2394 mpx->color_stack[mpx->color_stack_depth][k] = buf[l];
\r
2399 mpx->color_stack[mpx->color_stack_depth][k] = ')';
\r
2400 mpx->color_stack[mpx->color_stack_depth][k+1] = 0;
\r
2402 @ Binary-search the |named_colors| array, then push the found color onto
\r
2405 @<Handle a named color push command@>=
\r
2406 for (k = l;k<=len - 1;k++) {
\r
2407 buf[k - l] = xchr(buf[k]);
\r
2411 l = 1; r = mpx->num_named_colors;
\r
2413 while ( (l <= r) && ! found ) {
\r
2414 m = (l + r) / 2; k = strcmp((char *)(buf), mpx->named_colors[m].name);
\r
2416 mpx->color_stack[mpx->color_stack_depth]=xstrdup(mpx->named_colors[m].value);
\r
2418 } else if ( k < 0 ) {
\r
2425 color_warn_two("non-hardcoded color \"%s\" in \"color push\" command", buf);
\r
2426 mpx->color_stack[mpx->color_stack_depth]=xstrdup((char *)(buf));
\r
2429 @ Last but not least, this code snippet prints a \.{withcolor} specifier
\r
2430 for the top of the color stack, if the stack is nonempty.
\r
2432 @<Print a \.{withcolor} specifier if appropriate@>=
\r
2433 if ( mpx->color_stack_depth > 0 ) {
\r
2434 fprintf(mpx->mpxfile," withcolor %s\n",mpx->color_stack[mpx->color_stack_depth]);
\r
2440 This program reads device-independent troff output files,
\r
2441 and converts them into a symbolic form understood by MetaPost. Some
\r
2442 of the code was borrowed from DVItoMP. It understands all the D? graphics
\r
2443 functions that dpost does but it ignores `x X' device control functions
\r
2444 such as `x X SetColor:...', `x X BeginPath:', and `x X DrawPath:...'.
\r
2446 The output file is a sequence of MetaPost picture expressions, one for every
\r
2447 page in the input file. It makes no difference where the input file comes
\r
2448 from, but it is intended to process the result of running eqn and troff on
\r
2449 the output of MPtoTR. Such a file contains one page for every btex...etex
\r
2450 block in the original input. This program then creates a corresponding
\r
2451 sequence of MetaPost picture expressions for use as an auxiliary input file.
\r
2452 Since MetaPost expects such files to have the extension .mpx, the output
\r
2453 is sometimes called an `mpx' file.
\r
2455 @d SHIFTS 100 /* maximum number of characters with special shifts */
\r
2456 @d MAXCHARS 256 /* character codes fall in the range 0..MAXCHARS-1 */
\r
2458 @d is_specchar(c) (!mpx->gflag && (c)<=2) /* does charcode c identify a special char? */
\r
2459 @d LWscale 0.03 /* line width for graphics as a fraction of pointsize */
\r
2460 @d YCORR 12.0 /* V coordinate of reference point in (big) points */
\r
2463 int next_specfnt[(max_fnums+1)]; /* used to link special fonts together */
\r
2464 int shiftchar[SHIFTS]; /* charcode of character to shift, else -1 */
\r
2465 float shifth[SHIFTS];
\r
2466 float shiftv[SHIFTS]; /* shift vals/fontsize (y is upward) */
\r
2467 int shiftptr; /* number of entries in shift tables */
\r
2468 int shiftbase[(max_fnums+1)]; /* initial index into shifth,shiftv,shiftchar */
\r
2469 int specfnt; /* int. num. of first special font (or FCOUNT) */
\r
2470 int *specf_tail ; /* tail of specfnt list |(*specf_tail==FCOUNT)| */
\r
2471 float cursize; /* current type size in (big) points */
\r
2472 unsigned int curfont; /* internal number for current font */
\r
2473 float Xslant; /* degrees additional slant for all fonts */
\r
2474 float Xheight; /* yscale fonts to this height if nonzero */
\r
2475 float sizescale; /* groff font size scaling factor */
\r
2476 int gflag; /* non-zero if using groff fonts */
\r
2477 float unit; /* (big) points per troff unit (0 when unset) */
\r
2479 @ @<Set initial...@>=
\r
2480 mpx->shiftptr = 0;
\r
2481 mpx->specfnt = (max_fnums+1);
\r
2482 mpx->specf_tail = &(mpx->specfnt);
\r
2484 mpx->lnno = 0; /* this is a reset */
\r
2486 mpx->h = 0; mpx->v = 0;
\r
2489 typedef char *(*mpx_file_finder)(MPX, const char *, const char *, int);
\r
2490 enum mpx_filetype {
\r
2491 mpx_tfm_format, /* kpse_tfm_format */
\r
2492 mpx_vf_format, /* kpse_vf_format */
\r
2493 mpx_trfontmap_format, /* kpse_mpsupport_format */
\r
2494 mpx_trcharadj_format, /* kpse_mpsupport_format */
\r
2495 mpx_desc_format, /* kpse_troff_font_format */
\r
2496 mpx_fontdesc_format, /* kpse_troff_font_format */
\r
2497 mpx_specchar_format /* kpse_mpsupport_format */
\r
2501 mpx_file_finder find_file;
\r
2503 @ @<Declarations@>=
\r
2504 static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype);
\r
2507 static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
\r
2509 if (mode[0] != 'r' || (! access (nam,R_OK)) || ftype) {
\r
2510 return strdup(nam);
\r
2515 @ @<Set initial...@>=
\r
2516 mpx->find_file = mpx_find_file;
\r
2518 @ @<Declarations@>=
\r
2519 static FILE *mpx_fsearch(MPX mpx, char *nam, int format);
\r
2522 static FILE *mpx_fsearch(MPX mpx, char *nam, int format) {
\r
2524 char *fname = (mpx->find_file)(mpx, nam, "r", format);
\r
2526 f = fopen(fname, "rb");
\r
2527 mpx_report(mpx,"%p = fopen(%s,\"rb\")",f, fname);
\r
2532 @ Hash tables (or rather: AVL lists)
\r
2541 static int mpx_comp_name (const void *pa, const void *pb, void *p) {
\r
2543 return strcmp (((const avl_entry *) pa)->name,
\r
2544 ((const avl_entry *) pb)->name);
\r
2548 static struct avl_table *mpx_avl_create (MPX mpx) {
\r
2549 struct avl_table *t;
\r
2550 t = avl_create(mpx_comp_name, NULL, NULL);
\r
2552 mpx_abort(mpx, "Memory allocation failure");
\r
2557 @ The only two operations on AVL lists are finding already existing
\r
2558 items, or interning new items. Finding is handled by explicit |avl_find|
\r
2559 calls where needed, but it is wise to have a wrapper around |avl_probe|
\r
2560 to check for memory errors.
\r
2563 static avl_entry * mpx_avl_probe(MPX mpx, struct avl_table *tab, avl_entry *p) {
\r
2564 avl_entry *r = (avl_entry *)avl_probe(tab,p);
\r
2566 mpx_abort(mpx,"Memory allocation failure");
\r
2572 @ Scanning Numbers
\r
2574 The standard functions atoi(), atof(), and sscanf() provide ways of reading
\r
2575 numbers from strings but they give no indication of how much of the string
\r
2576 is consumed. These homemade versions don't parse scientific notation.
\r
2579 char *arg_tail; /* char after the number just gotten; NULL on failure */
\r
2582 static int mpx_get_int(MPX mpx, char *s) {
\r
2583 register int i, d, neg;
\r
2586 for (neg = 0;; s++) {
\r
2589 else if (*s != ' ' && *s != '\t')
\r
2592 if (i = *s - '0', 0 > i || i > 9)
\r
2594 while (d = *++s - '0', 0 <= d && d <= 9)
\r
2596 mpx->arg_tail = s;
\r
2597 return neg ? -i : i;
\r
2599 mpx->arg_tail = NULL;
\r
2603 @ GROFF font description files use octal character codes
\r
2604 |groff_font(5)|: The code can be any integer. If it starts with
\r
2605 a 0 it will be interpreted as octal; if it starts with 0x
\r
2606 or 0X it will be intepreted as hexadecimal.
\r
2609 static int mpx_get_int_map(MPX mpx, char *s) {
\r
2613 i = strtol(s, &(mpx->arg_tail), 0);
\r
2614 if (s == mpx->arg_tail)
\r
2618 mpx->arg_tail = NULL;
\r
2622 @ Troff output files contain few if any non-integers, but this program is
\r
2623 prepared to read floats whenever they seem reasonable; i.e., when the
\r
2624 number is not being used for character positioning. (For non-PostScript
\r
2625 applications h and v are usually in pixels and should be integers.)
\r
2628 static float mpx_get_float(MPX mpx, char *s) {
\r
2629 register int d, neg, digits;
\r
2630 register float x, y;
\r
2634 for (neg = 0;; s++) {
\r
2637 else if (*s != ' ' && *s != '\t')
\r
2641 while (d = *s - '0', 0 <= d && d <= 9) {
\r
2648 while (d = *++s - '0', 0 <= d && d <= 9) {
\r
2655 if (digits == 0) {
\r
2656 mpx->arg_tail = NULL;
\r
2659 mpx->arg_tail = s;
\r
2660 return neg ? -x : x;
\r
2663 @ GROFF font description files have metrics field
\r
2664 of comma-separated integers. Traditional troff
\r
2665 have a float in this position. The value is not
\r
2666 used anyway - thus just skip the value,
\r
2667 eat all non-space chars.
\r
2670 static float mpx_get_float_map(MPX mpx, char *s) {
\r
2672 while (isspace(*s))
\r
2674 while (!isspace(*s) && *s)
\r
2677 mpx->arg_tail = s;
\r
2682 @ Reading Initialization Files
\r
2684 Read the database file, reserve internal font numbers and set
\r
2685 the |font_name| entries. Each line in the database file contains
\r
2686 |<troff-name>\t,PostScript-name>\t<TeX-name>|
\r
2688 |<troff-name>\t,PostScript-name>|
\r
2689 if the \TeX\ name matches the PostScript name. (|\t| means one or more tabs.)
\r
2692 struct avl_table *trfonts;
\r
2695 static void mpx_read_fmap(MPX mpx, char *dbase) {
\r
2698 char *nam; /* a font name being read */
\r
2701 fin = mpx_fsearch(mpx,dbase, mpx_trfontmap_format);
\r
2703 mpx_abort(mpx,"Cannot find %s", dbase);
\r
2705 mpx->trfonts = mpx_avl_create (mpx);
\r
2706 while ((buf = mpx_getline(mpx,fin)) != NULL) {
\r
2707 if (mpx->nfonts == (max_fnums+1))
\r
2708 mpx_abort(mpx,"Need to increase max_fnums");
\r
2710 while (*buf && *buf != '\t')
\r
2712 tmp = xmalloc(sizeof(avl_entry),1);
\r
2714 tmp->num = mpx->nfonts++;
\r
2715 (void)mpx_avl_probe (mpx,mpx->trfonts, tmp) ;
\r
2718 do buf++; while (*buf == '\t');
\r
2719 while (*buf && *buf != '\t'); buf++; /* skip over psname */
\r
2720 do buf++; while (*buf == '\t');
\r
2723 while (*buf); buf++;
\r
2725 mpx->font_name[tmp->num] = xstrdup(nam);
\r
2726 mpx->font_num[tmp->num] = -1; /* indicate font is not mounted */
\r
2729 mpx_fclose(mpx,fin);
\r
2733 @ Some characters need their coordinates shifted in order to agree with
\r
2734 troff's view of the world. Logically, this information belongs in the
\r
2735 font description files but it actually resides in a PostScript prolog
\r
2736 that the troff output processor dpost reads. Since that file is in
\r
2737 PostScript and subject to change, we read the same information from
\r
2738 a small auxiliary file that gives shift amounts relative to the font
\r
2739 size with y upward.
\r
2742 The PostScript prologue in GNU groff's font directory does not
\r
2743 contain any character shift information, so the following function
\r
2744 becomes redundant. Simply keeping an empty "trchars.adj" file
\r
2745 around will do fine without requiring any changes to this program.
\r
2748 static void mpx_read_char_adj(MPX mpx, char *adjfile) {
\r
2751 avl_entry tmp, *p;
\r
2754 fin = mpx_fsearch(mpx,adjfile, mpx_trcharadj_format);
\r
2756 mpx_abort(mpx,"Cannot find %s", adjfile);
\r
2758 for (i = 0; i < mpx->nfonts; i++)
\r
2759 mpx->shiftbase[i] = 0;
\r
2760 while (fgets(buf, 200, fin) != NULL) {
\r
2761 if (mpx->shiftptr == SHIFTS - 1)
\r
2762 mpx_abort(mpx,"Need to increase SHIFTS");
\r
2763 if (buf[0] != ' ' && buf[0] != '\t') {
\r
2764 for (i = 0; buf[i] != '\0'; i++)
\r
2765 if (buf[i] == '\n')
\r
2767 mpx->shiftchar[mpx->shiftptr++] = -1;
\r
2769 p = (avl_entry *)avl_find (mpx->trfonts, &tmp);
\r
2771 mpx_abort(mpx,"%s refers to unknown font %s", adjfile, buf);
\r
2772 mpx->shiftbase[p->num] = mpx->shiftptr;
\r
2775 mpx->shiftchar[mpx->shiftptr] = mpx_get_int(mpx,buf);
\r
2776 mpx->shifth[mpx->shiftptr] = mpx_get_float(mpx,mpx->arg_tail);
\r
2777 mpx->shiftv[mpx->shiftptr] = -mpx_get_float(mpx,mpx->arg_tail);
\r
2778 if (mpx->arg_tail == NULL)
\r
2779 mpx_abort(mpx,"Bad shift entry : \"%s\"", buf);
\r
2783 mpx->shiftchar[mpx->shiftptr++] = -1;
\r
2784 mpx_fclose(mpx,fin);
\r
2787 @ Read the DESC file of the troff device to gather information
\r
2788 about sizescale and whether running under groff.
\r
2790 Ignore all commands not specially handled. This relieves
\r
2791 of collecting commands without arguments here and also
\r
2792 makes the program more robust in case of future DESC
\r
2796 static void mpx_read_desc(MPX mpx) {
\r
2797 const char *const k1[] = {
\r
2798 "res", "hor", "vert", "unitwidth", "paperwidth",
\r
2799 "paperlength", "biggestfont", "spare2", "encoding",
\r
2802 const char *const g1[] = {
\r
2803 "family", "paperheight", "postpro", "prepro",
\r
2804 "print", "image_generator", "broken",
\r
2811 fp = mpx_fsearch(mpx,"DESC", mpx_desc_format);
\r
2813 mpx_abort(mpx,"Cannot find DESC");
\r
2814 while (fscanf(fp, "%199s", cmd) != EOF) {
\r
2815 if (*cmd == '#') {
\r
2816 while ((i = getc(fp)) != EOF && i != '\n');
\r
2819 if (strcmp(cmd, "fonts") == 0) {
\r
2820 if (fscanf(fp, "%d", &n) != 1)
\r
2822 for (i = 0; i < n; i++)
\r
2823 if (fscanf(fp, "%*s") == EOF)
\r
2825 } else if (strcmp(cmd, "sizes") == 0) {
\r
2826 while (fscanf(fp, "%d", &n) == 1 && n != 0);
\r
2827 } else if (strcmp(cmd, "styles") == 0 ||
\r
2828 strcmp(cmd, "papersize") == 0) {
\r
2830 while ((i = getc(fp)) != EOF && i != '\n');
\r
2831 } else if (strcmp(cmd, "sizescale") == 0) {
\r
2832 if (fscanf(fp, "%d", &n) == 1)
\r
2833 mpx->sizescale = n;
\r
2835 } else if (strcmp(cmd, "charset") == 0) {
\r
2838 for (i = 0; k1[i]; i++)
\r
2839 if (strcmp(cmd, k1[i]) == 0) {
\r
2840 if (fscanf(fp, "%*s") == EOF)
\r
2845 for (i = 0; g1[i]; i++)
\r
2846 if (strcmp(cmd, g1[i]) == 0) {
\r
2847 if (fscanf(fp, "%*s") == EOF)
\r
2857 @ Given one line from the character description file for the font with
\r
2858 internal number f, save the appropriate data in the charcodes[f] table.
\r
2859 A return value of zero indicates a syntax error.
\r
2862 GNU groff uses an extended font description file format documented
\r
2863 in |groff_font(5)|. In order to allow parsing of groff's font files,
\r
2864 this function needs to be rewritten as follows:
\r
2866 \item{1.}The `metrics' field parsed by |mpx_get_float(lin);| may include
\r
2867 a comma-separated list of up to six decimal integers rather
\r
2868 than just a single floating-point number.
\r
2870 \item{2.}The `charcode' field parsed by |lastcode = mpx_get_int(arg_tail);|
\r
2871 may be given either in decimal, octal, or hexadecimal format.
\r
2874 struct avl_table *charcodes[(max_fnums+1)]; /* hash tables for translating char names */
\r
2877 static int mpx_scan_desc_line(MPX mpx, int f, char *lin) {
\r
2878 static int lastcode;
\r
2882 while (*lin != ' ' && *lin != '\t' && *lin != '\0')
\r
2884 s = xmalloc(lin-t+1,1);
\r
2885 strncpy(s,t,lin-t);
\r
2886 while (*lin == ' ' || *lin == '\t')
\r
2888 if (*lin == '"') {
\r
2889 if (lastcode < MAXCHARS) {
\r
2890 tmp = xmalloc(sizeof(avl_entry),1);
\r
2892 tmp->num = lastcode;
\r
2893 (void)mpx_avl_probe (mpx, mpx->charcodes[f],tmp);
\r
2896 (void) mpx_get_float_map(mpx,lin);
\r
2897 (void) mpx_get_int(mpx,mpx->arg_tail);
\r
2898 lastcode = mpx_get_int_map(mpx,mpx->arg_tail);
\r
2899 if (mpx->arg_tail == NULL)
\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
2911 @ Read the font description file for the font with the given troff name
\r
2912 and update the data structures. The result is the internal font number.
\r
2915 static int mpx_read_fontdesc(MPX mpx, char *nam) { /* troff name */
\r
2917 avl_entry tmp, *p;
\r
2918 FILE *fin; /* input file */
\r
2919 int f; /* internal font number */
\r
2921 if (mpx->unit == 0.0)
\r
2922 mpx_abort(mpx, "Resolution is not set soon enough");
\r
2924 p = (avl_entry *)avl_find (mpx->trfonts, &tmp);
\r
2926 mpx_abort(mpx, "Font was not in map file");
\r
2928 fin = mpx_fsearch(mpx, nam, mpx_fontdesc_format);
\r
2930 mpx_abort(mpx,"Cannot find %s", nam);
\r
2932 if (fgets(buf, 200, fin) == NULL)
\r
2933 mpx_abort(mpx, "Description file for %s ends unexpectedly", nam);
\r
2934 if (strncmp(buf, "special", 7) == 0) {
\r
2935 *(mpx->specf_tail) = f;
\r
2936 mpx->next_specfnt[f] = (max_fnums+1);
\r
2937 mpx->specf_tail = &(mpx->next_specfnt[f]);
\r
2938 } else if (strncmp(buf, "charset", 7) == 0)
\r
2941 mpx->charcodes[f] = mpx_avl_create (mpx);
\r
2942 while (fgets(buf, 200, fin) != NULL)
\r
2943 if (mpx_scan_desc_line(mpx, f, buf) == 0)
\r
2944 mpx_abort(mpx, "%s has a bad line in its description file: %s", nam, buf);
\r
2945 mpx_fclose(mpx,fin);
\r
2949 @ Page and Character Output
\r
2952 boolean graphics_used; /* nonzero if any graphics seen on this page */
\r
2954 float dmp_str_v; /* corrected start pos for current out string */
\r
2955 float dmp_str_h2; /* where the current output string ends */
\r
2956 float str_size; /* point size for this text string */
\r
2959 @ Print any transformations required by the current Xslant and Xheight settings.
\r
2962 static void mpx_slant_and_ht(MPX mpx);
\r
2965 static void mpx_slant_and_ht(MPX mpx) {
\r
2967 if (mpx->Xslant != 0.0) {
\r
2968 fprintf(mpx->mpxfile, " slanted%.5f", mpx->Xslant);
\r
2971 if (mpx->Xheight != mpx->cursize && mpx->Xheight != 0.0 && mpx->cursize != 0.0) {
\r
2972 fprintf(mpx->mpxfile, " yscaled%.4f", mpx->Xheight / mpx->cursize);
\r
2976 fprintf(mpx->mpxfile, "\n ");
\r
2980 @ Output character number c in the font with internal number f.
\r
2983 static void mpx_set_num_char(MPX mpx, int f, int c) {
\r
2984 float hh, vv; /* corrected versions of h, v */
\r
2989 for (i = mpx->shiftbase[f]; mpx->shiftchar[i] >= 0; i++)
\r
2990 if (mpx->shiftchar[i] == c) {
\r
2991 hh += (mpx->cursize / mpx->unit) * mpx->shifth[i];
\r
2992 vv += (mpx->cursize / mpx->unit) * mpx->shiftv[i];
\r
2995 if (hh - mpx->dmp_str_h2 >= 1.0 || mpx->dmp_str_h2 - hh >= 1.0 ||
\r
2996 vv - mpx->dmp_str_v >= 1.0 || mpx->dmp_str_v - vv >= 1.0 ||
\r
2997 f != mpx->str_f || mpx->cursize != mpx->str_size) {
\r
2998 if (mpx->str_f >= 0)
\r
2999 mpx_finish_last_char(mpx);
\r
3000 else if (!mpx->fonts_used)
\r
3001 mpx_prepare_font_use(mpx); /* first font usage on this page */
\r
3002 if (!mpx->font_used[f])
\r
3003 mpx_first_use(mpx,f); /* first use of font f on this page */
\r
3004 fprintf(mpx->mpxfile, "_s((");
\r
3005 mpx->print_col = 3;
\r
3007 mpx->dmp_str_v = vv;
\r
3008 mpx->dmp_str_h1 = hh;
\r
3009 mpx->str_size = mpx->cursize;
\r
3011 mpx_print_char(mpx,c);
\r
3012 mpx->dmp_str_h2 = hh + char_width(f,c);
\r
3015 @ Output a string.
\r
3018 static void mpx_set_string(MPX mpx, char *cname) {
\r
3019 float hh; /* corrected version of h, current horisontal position */
\r
3024 mpx_set_num_char(mpx,mpx->curfont, *cname);
\r
3025 hh += char_width(mpx->curfont,(int)*cname);
\r
3026 while (*++cname) {
\r
3027 mpx_print_char(mpx,*cname);
\r
3028 hh += char_width(mpx->curfont,(int)*cname);
\r
3030 mpx->h = (double)floor(hh+0.5);
\r
3031 mpx_finish_last_char(mpx);
\r
3034 @ Special Characters
\r
3036 Given the troff name of a special character, this routine finds its
\r
3037 definition and copies it to the MPX file. It also finds the name of
\r
3038 the vardef macro and returns that name. The name should be C.<something>.
\r
3041 TH: A bit of trickery is added here for case-insensitive
\r
3042 file systems. This aliasing allows the CHARLIB directory
\r
3043 to exist on DVDs, for example.
\r
3044 It is a hack, I know. I've stuck to names on TeXLive.
\r
3046 @d test_redo_search do {
\r
3048 deff = mpx_fsearch(mpx, cname, mpx_specchar_format);
\r
3052 static char *mpx_copy_spec_char(MPX mpx, char *cname) {
\r
3056 char specintro[] = "vardef "; /* MetaPost name follows this */
\r
3057 unsigned k = 0; /* how much of specintro so far */
\r
3058 if (strcmp(cname, "ao") == 0) {
\r
3059 deff = mpx_fsearch(mpx, "ao.x", mpx_specchar_format);
\r
3061 } else if (strcmp(cname, "lh") == 0) {
\r
3062 deff = mpx_fsearch(mpx, "lh.x", mpx_specchar_format);
\r
3064 } else if (strcmp(cname, "~=") == 0) {
\r
3065 deff = mpx_fsearch(mpx, "twiddle", mpx_specchar_format);
\r
3068 deff = mpx_fsearch(mpx, cname, mpx_specchar_format);
\r
3071 mpx_abort(mpx, "No vardef in charlib/%s", cname);
\r
3073 while (k < strlen(specintro)) {
\r
3074 if ((c = getc(deff)) == EOF)
\r
3075 mpx_abort(mpx, "No vardef in charlib/%s", cname);
\r
3076 putc(c, mpx->mpxfile);
\r
3077 if (c == specintro[k])
\r
3082 s = xmalloc(mpx->bufsize,1);
\r
3084 while ((c = getc(deff)) != '(') {
\r
3086 mpx_abort(mpx, "vardef in charlib/%s has no arguments", cname);
\r
3087 putc(c, mpx->mpxfile);
\r
3090 putc(c, mpx->mpxfile);
\r
3092 while ((c = getc(deff)) != EOF);
\r
3093 putc(c, mpx->mpxfile);
\r
3098 @ When given a character name instead of a number, we need to check if
\r
3099 it is a special character and download the definition if necessary.
\r
3100 If the character is not in the current font we have to search the special
\r
3104 struct avl_table *spec_tab;
\r
3106 @ The |spec_tab| avl table combines character names with macro names.
\r
3115 static void mpx_set_char(MPX mpx, char *cname) {
\r
3117 avl_entry tmp, *p;
\r
3120 if (*cname == ' ' || *cname == '\t')
\r
3124 p = avl_find(mpx->charcodes[f], &tmp);
\r
3126 for (f = mpx->specfnt; f != (max_fnums+1); f = mpx->next_specfnt[f]) {
\r
3127 p = avl_find(mpx->charcodes[f], &tmp);
\r
3131 mpx_abort(mpx, "There is no character %s", cname);
\r
3135 if (!is_specchar(c)) {
\r
3136 mpx_set_num_char(mpx, f, c);
\r
3138 if (mpx->str_f >= 0)
\r
3139 mpx_finish_last_char(mpx);
\r
3140 if (!mpx->fonts_used)
\r
3141 mpx_prepare_font_use(mpx);
\r
3142 if (!mpx->font_used[f])
\r
3143 mpx_first_use(mpx, f);
\r
3144 if (mpx->spec_tab)
\r
3145 mpx->spec_tab = mpx_avl_create (mpx);
\r
3146 sp = xmalloc(sizeof(spec_entry),1);
\r
3149 sp = (spec_entry *)avl_probe(mpx->spec_tab,sp);
\r
3151 mpx_abort(mpx,"Memory allocation failure");
\r
3152 if (sp->mac == NULL) {
\r
3153 sp->mac = mpx_copy_spec_char(mpx, cname); /* this won't be NULL */
\r
3155 fprintf(mpx->mpxfile, "_s(%s(n%d)", sp->mac, mpx->font_num[f]);
\r
3156 mpx_slant_and_ht(mpx);
\r
3157 fprintf(mpx->mpxfile, ",%.5f,%.4f,%.4f);\n",
\r
3158 (mpx->cursize/mpx->font_design_size[f])*1.00375, mpx->h*mpx->unit, YCORR-mpx->v*mpx->unit);
\r
3162 @ Font Definitions
\r
3164 Mount the font with troff name nam at external font number n and read any
\r
3165 necessary font files.
\r
3168 static void mpx_do_font_def(MPX mpx, int n, char *nam) {
\r
3171 avl_entry tmp, *p;
\r
3173 p = (avl_entry *) avl_find (mpx->trfonts, &tmp);
\r
3175 mpx_abort(mpx, "Font %s was not in map file", nam);
\r
3177 if ( mpx->info_base[f]==max_widths ) {
\r
3178 mpx_read_fontdesc(mpx, nam);
\r
3179 mpx->cur_name = xstrdup(mpx->font_name[f]);
\r
3180 if (! mpx_open_tfm_file(mpx) )
\r
3181 font_abort("No TFM file found for ",f);
\r
3182 @.no TFM file found@>
\r
3183 mpx_in_TFM(mpx, f);
\r
3185 for (k = 0; k < mpx->nfonts; k++)
\r
3186 if (mpx->font_num[k] == n)
\r
3187 mpx->font_num[k] = -1;
\r
3188 mpx->font_num[f] = n;
\r
3189 @<Do any other initialization required for the new font |f|@>;
\r
3194 @ Time on `makepath pencircle'
\r
3196 Given the control points of a cubic Bernstein polynomial, evaluate it at t.
\r
3198 @d Speed ((float) (PI/4.0))
\r
3201 static float mpx_b_eval(const float *xx, float t) {
\r
3203 register int i, j;
\r
3204 for (i = 0; i <= 3; i++)
\r
3206 for (i = 3; i > 0; i--)
\r
3207 for (j = 0; j < i; j++)
\r
3208 zz[j] += t * (zz[j + 1] - zz[j]);
\r
3213 @ Find the direction angle at time t on the path `makepath pencircle'.
\r
3214 The tables below give the Bezier control points for MetaPost's cubic
\r
3215 approximation to the first octant of a unit circle.
\r
3218 static const float xx[] = { 1.0, 1.0, 0.8946431597, 0.7071067812 };
\r
3219 static const float yy[] = { 0.0, 0.2652164899, 0.5195704026, 0.7071067812 };
\r
3222 static float mpx_circangle(float t) {
\r
3226 return (float) atan(mpx_b_eval(yy, t) /
\r
3227 mpx_b_eval(xx, t)) + ti * Speed;
\r
3231 @ Find the spline parameter where `makepath pencircle' comes closest to
\r
3232 (cos(a)/2,sin(a)/2).
\r
3235 static float mpx_circtime(float a) {
\r
3239 for (i = 2; --i >= 0;)
\r
3240 t += (a - mpx_circangle(t)) / Speed;
\r
3250 float gy; /* current point for graphics (init. (h,YCORR/mpx->unit-v) */
\r
3253 static void mpx_prepare_graphics(MPX mpx) {
\r
3255 fprintf(mpx->mpxfile, "vardef _D(expr _d)expr _q =\n");
\r
3256 fprintf(mpx->mpxfile,
\r
3257 " addto _p doublepath _q withpen pencircle scaled _d; enddef;\n");
\r
3258 mpx->graphics_used = true;
\r
3262 @ This function prints the current position (gx,gy). Then if it can read dh dv
\r
3263 from string s, it increments (gx,gy) and prints "--". By returning the rest
\r
3264 of the string s or NULL if nothing could be read from s, it provides the
\r
3265 argument for the next iteration.
\r
3268 static char *mpx_do_line(MPX mpx, char *s) {
\r
3271 fprintf(mpx->mpxfile, "(%.3f,%.3f)", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
\r
3272 dh = mpx_get_float(mpx, s);
\r
3273 dv = mpx_get_float(mpx, mpx->arg_tail);
\r
3274 if (mpx->arg_tail == NULL)
\r
3278 fprintf(mpx->mpxfile, "--\n");
\r
3279 return mpx->arg_tail;
\r
3283 @ Function |spline_seg()| reads two pairs of (dh,dv) increments and prints the
\r
3284 corresponding quadratic B-spline segment, leaving the ending point to be
\r
3285 printed next time. The return value is the string with the first (dh,dv)
\r
3286 pair lopped off. If only one pair of increments is found, we prepare to
\r
3287 terminate the iteration by printing last time's ending point and returning
\r
3291 static char * mpx_spline_seg(MPX mpx, char *s) {
\r
3292 float dh1, dv1, dh2, dv2;
\r
3294 dh1 = mpx_get_float(mpx, s);
\r
3295 dv1 = mpx_get_float(mpx, mpx->arg_tail);
\r
3296 if (mpx->arg_tail == NULL)
\r
3297 mpx_abort(mpx, "Missing spline increments");
\r
3298 s = mpx->arg_tail;
\r
3299 fprintf(mpx->mpxfile, "(%.3f,%.3f)", (mpx->gx + .5 * dh1) * mpx->unit,
\r
3300 (mpx->gy - .5 * dv1) * mpx->unit);
\r
3303 dh2 = mpx_get_float(mpx, s);
\r
3304 dv2 = mpx_get_float(mpx, mpx->arg_tail);
\r
3305 if (mpx->arg_tail == NULL)
\r
3307 fprintf(mpx->mpxfile, "..\ncontrols (%.3f,%.3f) and (%.3f,%.3f)..\n",
\r
3308 (mpx->gx - dh1 / 6.0) * mpx->unit, (mpx->gy + dv1 / 6.0) * mpx->unit,
\r
3309 (mpx->gx + dh2 / 6.0) * mpx->unit, (mpx->gy - dv2 / 6.0) * mpx->unit);
\r
3314 @ Draw an ellipse with the given major and minor axes.
\r
3317 static void mpx_do_ellipse(MPX mpx, float a, float b) {
\r
3319 fprintf(mpx->mpxfile, "makepath(pencircle xscaled %.3f\n yscaled %.3f",
\r
3320 a * mpx->unit, b * mpx->unit);
\r
3321 fprintf(mpx->mpxfile, " shifted (%.3f,%.3f));\n", (mpx->gx + .5 * a) * mpx->unit,
\r
3322 mpx->gy * mpx->unit);
\r
3327 @ Draw a counter-clockwise arc centered at (cx,cy) with initial and final radii
\r
3328 (ax,ay) and (bx,by) respectively.
\r
3332 void mpx_do_arc(MPX mpx, float cx, float cy, float ax, float ay, float bx, float by) {
\r
3335 t1 = mpx_circtime(atan2(ay, ax));
\r
3336 t2 = mpx_circtime(atan2(by, bx));
\r
3339 fprintf(mpx->mpxfile, "subpath (%.5f,%.5f) of\n", t1, t2);
\r
3340 fprintf(mpx->mpxfile,
\r
3341 " makepath(pencircle scaled %.3f shifted (%.3f,%.3f));\n",
\r
3342 2.0 * sqrt(ax * ax + ay * ay) * mpx->unit, cx * mpx->unit, cy * mpx->unit);
\r
3343 mpx->gx = cx + bx;
\r
3344 mpx->gy = cy + by;
\r
3349 @ String s is everything following the initial `D' in a troff graphics command.
\r
3352 static void mpx_do_graphic(MPX mpx, char *s) {
\r
3353 float h1, v1, h2, v2;
\r
3355 mpx_finish_last_char(mpx);
\r
3356 /* GROFF uses Fd to set fill color for solid drawing objects to the
\r
3357 default, so just ignore that.
\r
3359 if (s[0] == 'F' && s[1] == 'd')
\r
3361 mpx->gx = (float) mpx->h;
\r
3362 mpx->gy = YCORR / mpx->unit - ((float) mpx->v);
\r
3363 if (!mpx->graphics_used)
\r
3364 mpx_prepare_graphics(mpx);
\r
3365 fprintf(mpx->mpxfile, "D(%.4f) ", LWscale * mpx->cursize);
\r
3368 h1 = mpx_get_float(mpx,s);
\r
3369 if (mpx->arg_tail == NULL)
\r
3370 mpx_abort(mpx,"Bad argument in %s", s-2);
\r
3371 mpx_do_ellipse(mpx,h1, h1);
\r
3374 h1 = mpx_get_float(mpx,s);
\r
3375 v1 = mpx_get_float(mpx,mpx->arg_tail);
\r
3376 if (mpx->arg_tail == NULL)
\r
3377 mpx_abort(mpx,"Bad argument in %s", s - 2);
\r
3378 mpx_do_ellipse(mpx,h1, v1);
\r
3381 fprintf(mpx->mpxfile, "reverse ");
\r
3382 /* fall through */
\r
3384 h1 = mpx_get_float(mpx,s);
\r
3385 v1 = mpx_get_float(mpx,mpx->arg_tail);
\r
3386 h2 = mpx_get_float(mpx,mpx->arg_tail);
\r
3387 v2 = 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_arc(mpx,mpx->gx + h1, mpx->gy - v1, -h1, v1, h2, -v2);
\r
3395 s = mpx_do_line(mpx,s);
\r
3396 fprintf(mpx->mpxfile, ";\n");
\r
3400 s = mpx_spline_seg(mpx,s);
\r
3401 while (s != NULL);
\r
3402 fprintf(mpx->mpxfile, ";\n");
\r
3405 fprintf(mpx->mpxfile, "(%.3f,%.3f)--", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
\r
3407 s = mpx_spline_seg(mpx,s);
\r
3408 while (s != NULL);
\r
3409 fprintf(mpx->mpxfile, "--(%.3f,%.3f);\n", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
\r
3412 mpx_abort(mpx,"Unknown drawing function %s", s - 2);
\r
3414 mpx->h = (int) floor(mpx->gx + .5);
\r
3415 mpx->v = (int) floor(YCORR / mpx->unit + .5 - mpx->gy);
\r
3420 @ Interpreting Troff Output
\r
3423 static void mpx_change_font(MPX mpx, int f) {
\r
3424 for (mpx->curfont = 0; mpx->curfont < mpx->nfonts; mpx->curfont++)
\r
3425 if (mpx->font_num[mpx->curfont] == f)
\r
3427 mpx_abort(mpx,"Bad font setting");
\r
3431 @ String s0 is everything following the initial `x' in a troff device control
\r
3432 command. A zero result indicates a stop command.
\r
3435 static int mpx_do_x_cmd(MPX mpx, char *s0)
\r
3442 while (*s == ' ' || *s == '\t')
\r
3446 if (mpx->unit != 0.0)
\r
3447 mpx_abort(mpx,"Attempt to reset resolution");
\r
3448 while (*s != ' ' && *s != '\t')
\r
3450 mpx->unit = mpx_get_float(mpx,s);
\r
3451 if (mpx->unit <= 0.0)
\r
3452 mpx_abort(mpx,"Bad resolution: x %s", s0);
\r
3453 mpx->unit = 72.0 / mpx->unit;
\r
3456 while (*s != ' ' && *s != '\t')
\r
3458 n = mpx_get_int(mpx,s);
\r
3459 if (mpx->arg_tail == NULL)
\r
3460 mpx_abort(mpx,"Bad font def: x %s", s0);
\r
3461 s = mpx->arg_tail;
\r
3462 while (*s == ' ' || *s == '\t')
\r
3464 mpx_do_font_def(mpx,n, s);
\r
3469 while (*s != ' ' && *s != '\t')
\r
3471 mpx->Xheight = mpx_get_float(mpx,s);
\r
3472 /* GROFF troff output is scaled |groff_out(5)|:
\r
3473 The argument to the s command is in scaled
\r
3474 points (units of points/n, where n is the argument
\r
3475 to the sizescale command in the DESC file.) The
\r
3476 argument to the x Height command is also in scaled points.
\r
3477 sizescale for groff devps is 1000
\r
3479 if (mpx->sizescale) {
\r
3480 if (mpx->unit != 0.0)
\r
3481 mpx->Xheight *= mpx->unit; /* ??? */
\r
3483 mpx->Xheight /= mpx->sizescale;
\r
3485 if (mpx->Xheight == mpx->cursize)
\r
3486 mpx->Xheight = 0.0;
\r
3489 while (*s != ' ' && *s != '\t')
\r
3491 mpx->Xslant = mpx_get_float(mpx,s) * (PI / 180.0);
\r
3492 x = cos(mpx->Xslant);
\r
3493 if (-1e-4 < x && x < 1e-4)
\r
3494 mpx_abort(mpx,"Excessive slant");
\r
3495 mpx->Xslant = sin(mpx->Xslant) / x;
\r
3498 /* do nothing */ ;
\r
3504 @ This routine reads commands from the troff output file up to and including
\r
3505 the next `p' or `x s' command. It also calls |set_num_char()| and |set_char()|
\r
3506 to generate output when appropriate. A zero result indicates that there
\r
3507 are no more pages to do.
\r
3510 GNU groff uses an extended device-independent output file format
\r
3511 documented in |groff_out(5)|. In order to allow parsing of groff's
\r
3512 output files, this function either needs to be extended to support
\r
3513 the new command codes, or else the use of the "t" and "u" commands
\r
3514 must be disabled by removing the line "tcommand" from the DESC file
\r
3515 in the \$(prefix)/lib/groff/devps directory.
\r
3518 static int mpx_do_page (MPX mpx, FILE *trf) {
\r
3522 mpx->h = mpx->v = 0;
\r
3523 while ((buf = mpx_getline(mpx, trf)) != NULL) {
\r
3526 while (*c != '\0') {
\r
3534 mpx->cursize = mpx_get_float(mpx,c + 1);
\r
3535 /* GROFF troff output is scaled
\r
3536 |groff_out(5)|: The argument to the s command is in scaled
\r
3537 points (units of points/n, where n is the argument
\r
3538 to the sizescale command in the DESC file.) The
\r
3539 argument to the x Height command is also in scaled
\r
3541 sizescale for groff devps is 1000
\r
3543 if (mpx->sizescale) {
\r
3544 if (mpx->unit != 0.0)
\r
3545 mpx->cursize *= mpx->unit; /* ??? */
\r
3547 mpx->cursize /= mpx->sizescale;
\r
3551 mpx_change_font(mpx, mpx_get_int(mpx,c + 1));
\r
3555 mpx_abort(mpx, "Bad c command in troff output");
\r
3562 while (*cc != ' ' && *cc != '\t' && *cc != '\0');
\r
3565 mpx_set_num_char(mpx, mpx->curfont, mpx_get_int(mpx,c + 1));
\r
3568 mpx->h = mpx_get_int(mpx, c + 1);
\r
3571 mpx->v = mpx_get_int(mpx, c + 1);
\r
3574 mpx->h += mpx_get_int(mpx, c + 1);
\r
3577 mpx->v += mpx_get_int(mpx, c + 1);
\r
3589 if (c[1] < '0' || c[1] > '9' || c[2] == '\0')
\r
3590 mpx_abort(mpx, "Bad nnc command in troff output");
\r
3591 mpx->h += 10 * (c[0] - '0') + c[1] - '0';
\r
3598 (void) mpx_get_int(mpx, c + 1);
\r
3599 (void) mpx_get_int(mpx, mpx->arg_tail);
\r
3602 mpx_do_graphic(mpx, c + 1);
\r
3605 if (!mpx_do_x_cmd(mpx, c + 1))
\r
3611 /* GROFF uses this command to report filename */
\r
3614 /* GROFF uses this command to control color */
\r
3617 /* GROFF uses this command to output a word with additional
\r
3618 white space between characters, not implemented
\r
3620 mpx_abort(mpx, "Bad command in troff output\n"
\r
3621 "change the DESC file for your GROFF PostScript device, remove tcommand");
\r
3623 /* GROFF uses this command to output a word */
\r
3627 while (*cc != ' ' && *cc != '\t' && *cc != '\0');
\r
3630 mpx_set_string(mpx, ++c);
\r
3635 mpx_abort(mpx, "Bad command in troff output");
\r
3641 mpx_set_char(mpx, ++c);
\r
3646 c = mpx->arg_tail;
\r
3648 eoln: /* do nothing */ ;
\r
3654 @ Main Dmp Program
\r
3656 @d dbname "trfonts.map" /* file for table of troff \& TFM font names */
\r
3657 @d adjname "trchars.adj" /* file for character shift amounts */
\r
3660 static int mpx_dmp(MPX mpx, char *infile) {
\r
3662 FILE *trf = mpx_xfopen(mpx,infile, "r");
\r
3663 mpx_open_mpxfile(mpx);
\r
3664 fprintf(mpx->mpxfile, mpx->banner);
\r
3665 fprintf (mpx->mpxfile,"\n");
\r
3666 mpx_read_desc(mpx);
\r
3667 mpx_read_fmap(mpx,dbname);
\r
3669 mpx_read_char_adj(mpx,adjname);
\r
3670 if (mpx_do_page(mpx, trf)) {
\r
3672 @<Do initialization required before starting a new page@>;
\r
3673 mpx_start_picture(mpx);
\r
3674 more = mpx_do_page(mpx,trf);
\r
3675 mpx_stop_picture(mpx);
\r
3676 fprintf(mpx->mpxfile, "mpxbreak\n");
\r
3679 mpx_fclose(mpx,trf);
\r
3680 mpx_fclose(mpx,mpx->mpxfile);
\r
3681 if ( mpx->history<=mpx_cksum_trouble )
\r
3684 return mpx->history;
\r
3691 Make an MPX file from the labels in a MetaPost source file,
\r
3692 using mpto and either dvitomp (TeX) or dmp (troff).
\r
3694 Started from a shell script initially based on John Hobby's original
\r
3695 version, that was then translated to C by Akira Kakuto (Aug 1997,
\r
3696 Aug 2001), and updated and largely rewritten by Taco Hoekwater (Nov 2006).
\r
3699 Differences between the script and this C version:
\r
3701 The script trapped HUP, INT, QUIT and TERM for cleaning up
\r
3702 temporary files. This is a refinement, and not portable.
\r
3704 The script put its own directory in front of the
\r
3705 executable search PATH. This is not portable either, and
\r
3706 it seems a safe bet that normal users do not have 'mpto',
\r
3707 'dvitomp', or 'dmp' commands in their path.
\r
3709 The command-line '-troff' now also accepts an optional argument.
\r
3711 The troff infile for error diagnostics is renamed "mpxerr.i",
\r
3712 not plain "mpxerr".
\r
3714 The original script deleted mpx*.* in the cleanup process.
\r
3716 That is a bit harder in C, because it requires reading the contents
\r
3717 of the current directory. The current program assumes that
\r
3718 opendir(), readdir() and closedir() are known everywhere where
\r
3719 the function getcwd() exists (except on WIN32, where it uses
\r
3720 |_findfirst| \& co).
\r
3722 If this assumption is false, you can define |NO_GETCWD|, and makempx
\r
3723 will revert to trying to delete only a few known extensions
\r
3725 There is a -debug switch, preventing the removal of tmp files
\r
3727 @d TMPNAME_EXT(a,b) { strcpy(a,tmpname); strcat(a,b); }
\r
3731 #define TEXERR "mpxerr.tex"
\r
3732 #define DVIERR "mpxerr.dvi"
\r
3733 #define TROFF_INERR "mpxerr.i"
\r
3734 #define TROFF_OUTERR "mpxerr.t"
\r
3737 static void mpx_rename (MPX mpx, char *a, char *b) {
\r
3738 mpx_report(mpx,"renaming %s to %s",a,b);
\r
3749 static void mpx_default_erasetmp(MPX mpx) {
\r
3752 if (mpx->mode==mpx_tex_mode) {
\r
3753 wrk = xstrdup(mpx->tex);
\r
3754 p = strrchr(wrk, '.');
\r
3755 *p = '\0'; strcat(wrk, ".aux"); remove(wrk);
\r
3756 *p = '\0'; strcat(wrk, ".pdf"); remove(wrk);
\r
3757 *p = '\0'; strcat(wrk, ".toc"); remove(wrk);
\r
3758 *p = '\0'; strcat(wrk, ".idx"); remove(wrk);
\r
3759 *p = '\0'; strcat(wrk, ".ent"); remove(wrk);
\r
3760 *p = '\0'; strcat(wrk, ".out"); remove(wrk);
\r
3761 *p = '\0'; strcat(wrk, ".nav"); remove(wrk);
\r
3762 *p = '\0'; strcat(wrk, ".snm"); remove(wrk);
\r
3763 *p = '\0'; strcat(wrk, ".tui"); remove(wrk);
\r
3768 @ @<Declarations@>=
\r
3769 static void mpx_erasetmp(MPX mpx);
\r
3772 static void mpx_cleandir(MPX mpx, char *cur_path) {
\r
3775 struct _finddata_t c_file;
\r
3778 struct dirent *entry;
\r
3781 wrk = xstrdup(mpx->tex);
\r
3782 p = strrchr(wrk, '.');
\r
3783 *p = '\0'; /* now wrk is identical to tmpname */
\r
3786 strcat(cur_path,"/*");
\r
3787 if ((hFile = _findfirst (cur_path, &c_file)) == -1L) {
\r
3788 mpx_default_erasetmp(mpx);
\r
3790 if (strstr(c_file.name,wrk)==c_file.name)
\r
3791 remove(c_file.name);
\r
3792 while (_findnext (hFile, &c_file) != -1L) {
\r
3793 if (strstr(c_file.name,wrk)==c_file.name)
\r
3794 remove(c_file.name);
\r
3796 _findclose (hFile); /* no more entries => close directory */
\r
3799 if ((d = opendir(cur_path)) == NULL) {
\r
3800 mpx_default_erasetmp(mpx);
\r
3802 while ((entry = readdir (d)) != NULL) {
\r
3803 if (strstr(entry->d_name,wrk)==entry->d_name)
\r
3804 remove(entry->d_name);
\r
3813 @ It is important that |mpx_erasetmp| remains silent.
\r
3814 If it find trouble, it should just ignore it.
\r
3816 The string |cur_path| is a little bit larger than needed, because that
\r
3817 allows the win32 code in |cleandir| to add the slash and asterisk for
\r
3818 globbing without having to reallocate the variable first.
\r
3822 #define GETCWD _getcwd
\r
3824 #define GETCWD getcwd
\r
3826 static void mpx_erasetmp(MPX mpx) {
\r
3827 char cur_path[1024];
\r
3830 if (mpx->tex[0] != '\0') {
\r
3832 if(GETCWD(cur_path,1020) == NULL) {
\r
3833 mpx_default_erasetmp(mpx); /* don't know where we are */
\r
3835 mpx_cleandir(mpx,cur_path);
\r
3841 @* Running the external typesetters.
\r
3843 First, here is a helper for messaging.
\r
3846 static char *mpx_print_command (MPX mpx, int cmdlength, char **cmdline) {
\r
3851 for (i = 0; i < cmdlength ; i++) {
\r
3852 l += strlen(cmdline[i])+1;
\r
3854 s = xmalloc(l,1); t=s;
\r
3855 for (i = 0; i < cmdlength ; i++) {
\r
3856 if (i>0) *t++ = ' ';
\r
3857 t = strcpy(t,cmdline[i]);
\r
3858 t += strlen(cmdline[i]);
\r
3863 @ This function unifies the external program calling across Posix-like and Win32
\r
3867 static int do_spawn (MPX mpx, char *cmd, char **options) {
\r
3873 mpx_abort(mpx, "fork failed: %s", strerror(errno));
\r
3875 if(execvp(cmd, options))
\r
3876 mpx_abort(mpx, "exec failed: %s", strerror(errno));
\r
3878 if (wait(&retcode)==child) {
\r
3879 retcode = (WIFEXITED(retcode) ? WEXITSTATUS(retcode) : -1);
\r
3881 mpx_abort(mpx, "wait failed: %s", strerror(errno));
\r
3887 return spawnvp(P_WAIT, cmd, (const char **)options);
\r
3893 #define nuldev "nul"
\r
3895 #define nuldev "/dev/null"
\r
3897 static int mpx_run_command(MPX mpx, char *inname, char *outname, int count, char **cmdl) {
\r
3900 int sav_o, sav_i; /* for I/O redirection */
\r
3901 FILE *fr, *fw; /* read and write streams for the command */
\r
3903 if (count < 1 || cmdl == NULL || cmdl[0] == NULL)
\r
3904 return -1; /* return non-zero by default, signalling an error */
\r
3906 s = mpx_print_command(mpx,count, cmdl);
\r
3907 mpx_report(mpx,"running command %s", s);
\r
3910 fr = mpx_xfopen(mpx,(inname ? inname : nuldev), "r");
\r
3911 fw = mpx_xfopen(mpx,(outname ? outname : nuldev), "wb");
\r
3912 @<Save and redirect the standard I/O@>;
\r
3913 retcode = do_spawn(mpx,cmdl[0], cmdl);
\r
3914 @<Restore the standard I/O@>;
\r
3915 mpx_fclose(mpx,fr);
\r
3916 mpx_fclose(mpx,fw);
\r
3920 @ @ Running Troff is more likely than not a series of pipes that
\r
3921 feed input to each other. Makempx does all of this itself by using
\r
3922 temporary files inbetween. That means we have to juggle about with
\r
3923 |stdin| and |stdout|.
\r
3925 This is the only non-ansi C bit of makempx.
\r
3926 @^system dependencies@>
\r
3928 @<Save and redirect the standard I/O@>=
\r
3931 #define DUPP _dup2
\r
3936 sav_i = DUP(fileno(stdin));
\r
3937 sav_o = DUP(fileno(stdout));
\r
3938 DUPP(fileno(fr), fileno(stdin));
\r
3939 DUPP(fileno(fw), fileno(stdout))
\r
3941 @ @<Restore the standard I/O@>=
\r
3942 DUPP(sav_i, fileno(stdin));
\r
3944 DUPP(sav_o, fileno(stdout));
\r
3947 @ The allocation of the array pointed to by |cmdline_addr| is of
\r
3948 course much larger than is really needed, but it will still only be a
\r
3949 few hunderd bytes at the most, and this ensures that the separate
\r
3950 parts of the |maincmd| will all fit.
\r
3952 @d split_command(a,b) mpx_do_split_command(mpx,a,&b,' ')
\r
3953 @d split_pipes(a,b) mpx_do_split_command(mpx,a,&b,'|')
\r
3957 mpx_do_split_command(MPX mpx, char *maincmd, char ***cmdline_addr, char target) {
\r
3963 int in_string = 0;
\r
3964 if (strlen(maincmd) == 0)
\r
3966 i = sizeof(char *)*(strlen(maincmd)+1);
\r
3967 cmdline = xmalloc(i,1);
\r
3968 memset(cmdline,0,i);
\r
3969 *cmdline_addr = cmdline;
\r
3972 while (maincmd[i] == ' ')
\r
3974 cmd = xstrdup(maincmd);
\r
3976 for (; i <= strlen(maincmd); i++) {
\r
3977 if (in_string == 1) {
\r
3978 if (cmd[i] == '"') {
\r
3981 } else if (in_string == 2) {
\r
3982 if (cmd[i] == '\'') {
\r
3986 if (cmd[i] == '"') {
\r
3988 } else if (cmd[i] == '\'') {
\r
3990 } else if (cmd[i] == target) {
\r
3992 cmdline[ret++] = piece;
\r
3993 while (i < strlen(maincmd) && cmd[(i + 1)] == ' ')
\r
3995 piece = cmd + i + 1;
\r
4000 cmdline[ret++] = piece;
\r
4006 char *maincmd; /* TeX command name */
\r
4009 static void mpx_command_cleanup (MPX mpx, char **cmdline) {
\r
4011 xfree(cmdline[0]);
\r
4018 static void mpx_command_error (MPX mpx, int cmdlength, char **cmdline) {
\r
4019 char *s = mpx_print_command(mpx, cmdlength, cmdline);
\r
4020 mpx_command_cleanup(mpx, cmdline);
\r
4021 mpx_abort(mpx, "Command failed: %s; see mpxerr.log", s);
\r
4027 typedef struct makempx_options {
\r
4035 mpx_file_finder find_file;
\r
4036 } makempx_options;
\r
4037 int mp_makempx (makempx_options *mpxopt) ;
\r
4042 @d ERRLOG "mpxerr.log"
\r
4043 @d MPXLOG "makempx.log"
\r
4046 int mp_makempx (makempx_options *mpxopt) {
\r
4048 char **cmdline, **cmdbits;
\r
4051 char tmpname[] = "mpXXXXXX";
\r
4052 int cmdlength = 1;
\r
4053 int cmdbitlength = 1;
\r
4054 if (!mpxopt->debug) {
\r
4055 @<Check if mp file is newer than mpxfile, exit if not@>;
\r
4057 mpx = malloc(sizeof(struct mpx_data));
\r
4059 return mpx_fatal_error;
\r
4060 mpx_initialize(mpx);
\r
4061 mpx->banner = mpxopt->banner;
\r
4062 mpx->mode = mpxopt->mode;
\r
4063 mpx->debug = mpxopt->debug;
\r
4064 if (mpxopt->find_file!=NULL)
\r
4065 mpx->find_file = mpxopt->find_file;
\r
4066 if (mpxopt->cmd!=NULL)
\r
4067 mpx->maincmd = xstrdup(mpxopt->cmd); /* valgrind says this leaks */
\r
4068 mpx->mpname = xstrdup(mpxopt->mpname);
\r
4069 mpx->mpxname = xstrdup(mpxopt->mpxname);
\r
4070 @<Install and test the non-local jump buffer@>;
\r
4073 mpx->errfile = stderr;
\r
4075 mpx->errfile = mpx_xfopen(mpx,MPXLOG, "wb");
\r
4077 mpx->progname = "makempx";
\r
4078 @<Initialize the |tmpname| variable@>;
\r
4079 if (mpxopt->mptexpre == NULL)
\r
4080 mpxopt->mptexpre = xstrdup("mptexpre.tex");
\r
4081 @<Run |mpto| on the mp file@>;
\r
4082 if (mpxopt->cmd==NULL)
\r
4084 if (mpx->mode == mpx_tex_mode) {
\r
4085 @<Run |TeX| and set up |infile| or abort@>;
\r
4086 if (mpx_dvitomp(mpx, infile)) {
\r
4087 mpx_rename(mpx, infile,DVIERR);
\r
4089 remove(mpx->mpxname);
\r
4090 mpx_abort(mpx, "Dvi conversion failed: %s %s\n",
\r
4091 DVIERR, mpx->mpxname);
\r
4093 } else if (mpx->mode == mpx_troff_mode) {
\r
4094 @<Run |Troff| and set up |infile| or abort@>;
\r
4095 if (mpx_dmp(mpx, infile)) {
\r
4096 mpx_rename(mpx,infile, TROFF_OUTERR);
\r
4097 mpx_rename(mpx,mpx->tex, TROFF_INERR);
\r
4099 remove(mpx->mpxname);
\r
4100 mpx_abort(mpx, "Troff conversion failed: %s %s\n",
\r
4101 TROFF_OUTERR, mpx->mpxname);
\r
4104 mpx_fclose(mpx,mpx->mpxfile);
\r
4106 mpx_fclose(mpx,mpx->errfile);
\r
4107 if (!mpx->debug) {
\r
4112 mpx_erasetmp(mpx);
\r
4114 retcode = mpx->history;
\r
4115 mpx_xfree(mpx->buf);
\r
4116 mpx_xfree(mpx->maincmd);
\r
4117 for (i = 0; i < (int)mpx->nfonts; i++)
\r
4118 mpx_xfree(mpx->font_name[i]);
\r
4120 if (retcode == mpx_cksum_trouble)
\r
4125 @ \TeX\ has to operate on an actual input file, so we have to append
\r
4126 that to the command line.
\r
4128 @<Run |TeX| and set ...@>=
\r
4131 mpx->maincmd = xrealloc(mpx->maincmd,strlen(mpx->maincmd)+strlen(mpx->tex)+2,1);
\r
4132 strcat(mpx->maincmd, " ");
\r
4133 strcat(mpx->maincmd, mpx->tex);
\r
4134 cmdlength = split_command(mpx->maincmd, cmdline);
\r
4136 retcode = mpx_run_command(mpx, NULL, NULL, cmdlength, cmdline);
\r
4138 TMPNAME_EXT(log, ".log");
\r
4140 TMPNAME_EXT(infile, ".dvi");
\r
4143 mpx_rename(mpx,mpx->tex, TEXERR);
\r
4144 mpx_rename(mpx,log, ERRLOG);
\r
4145 mpx_command_error(mpx, cmdlength, cmdline);
\r
4147 mpx_command_cleanup(mpx, cmdline);
\r
4150 @ @<Run |Troff| and set ...@>=
\r
4152 char *cur_in, *cur_out;
\r
4153 char tmp_a[15], tmp_b[15];
\r
4154 TMPNAME_EXT(tmp_a, ".t");
\r
4155 TMPNAME_EXT(tmp_b, ".tmp");
\r
4156 cur_in = mpx->tex;
\r
4159 /* split the command in bits */
\r
4160 cmdbitlength = split_pipes(mpx->maincmd, cmdbits);
\r
4162 for (i = 0; i < cmdbitlength; i++) {
\r
4163 if (cmdline!=NULL) free(cmdline);
\r
4164 cmdlength = split_command(cmdbits[i], cmdline);
\r
4165 retcode = mpx_run_command(mpx, cur_in, cur_out, cmdlength, cmdline);
\r
4168 mpx_rename(mpx,mpx->tex, TROFF_INERR);
\r
4169 mpx_command_error(mpx, cmdlength, cmdline);
\r
4171 if (i < cmdbitlength - 1) {
\r
4179 strcpy(infile,cur_in);
\r
4182 if (tmp_a!=infile) { remove(tmp_a); }
\r
4183 if (tmp_b!=infile) { remove(tmp_b); }
\r
4186 @ If MPX file is up-to-date or if MP file does not exist, do nothing.
\r
4188 @<Check if mp file is newer than mpxfile, exit if not@>=
\r
4189 if (mpx_newer(mpxopt->mpname, mpxopt->mpxname))
\r
4194 @<Initialize the |tmpname| variable@>=
\r
4195 #ifdef HAVE_MKSTEMP
\r
4196 i = mkstemp(tmpname);
\r
4198 sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
\r
4204 #ifdef HAVE_MKTEMP
\r
4206 char *tmpstring = mktemp(tmpname);
\r
4207 if ((tmpstring == NULL) || strlen(tmpname) == 0) {
\r
4208 sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
\r
4210 /* this should not really be needed, but better
\r
4211 safe than sorry. */
\r
4212 if (tmpstring != tmpname) {
\r
4213 i = strlen(tmpstring);
\r
4215 strncpy(tmpname, tmpstring, i);
\r
4220 sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
\r