diff options
Diffstat (limited to 'dviware/ivd2dvi/ivd2dvi.c')
-rw-r--r-- | dviware/ivd2dvi/ivd2dvi.c | 957 |
1 files changed, 957 insertions, 0 deletions
diff --git a/dviware/ivd2dvi/ivd2dvi.c b/dviware/ivd2dvi/ivd2dvi.c new file mode 100644 index 0000000000..d2f68397b0 --- /dev/null +++ b/dviware/ivd2dvi/ivd2dvi.c @@ -0,0 +1,957 @@ +/* + * ivd2dvi. + * Copyright 1988 by Larry Denenberg. May be freely distributed as long + * as this notice is retained. + * + * This is a general discussion of ivd2dvi, assuming familiarity with + * the format of DVI files and with the semantics of the reflection + * commands (see Knuth and Mackay, "Mixing right-to-left text with + * left-to-right text" in TUGboat volume 8 number 1). Notation: let + * "BR" stand for the BEGIN_REFLECT command, let "ER" stand for + * END_REFLECT, let "a" stand for SET_097 (i.e. "typeset character 97," + * usually lowercase a), and similarly for the other lowercase letters. + * + * The simplest idea would be to rearrange reflected text somehow so + * that the right thing happens. Is it enough to reverse the commands? + * Certainly if we see "BR a b c ER" we can replace it with "c b a". + * But this works only so long as the reflected segment sticks to a + * small subset of DVI commands; "BR a FONT_00 b ER" can't in general be + * changed to "b FONT_00 a". We also get in trouble with commands that + * change the DVI registers (not to mention PUSH and POP!). + * + * Some of these difficulties could be overcome with more clever + * rearrangement. Unfortunately, there are situations that can't be + * handled by any such scheme: for example, you can't typeset the + * sequence "BR PUSH i POP w ER" without knowing about the widths of + * "i" and "w". The PUTn commands also lead to unsolvable problems. + * So we abandon this approach and resign ourselves to reading width + * data from TFM files. + * + * Here's the method we use: we read through the input DVI file, + * copying DVI commands to the output file. When we see a BR we stop + * copying and simply read the input looking for the matching ER. + * During this scan we keep track of the total width of the commands + * between the BR and ER. (Each character contributes its width as + * revealed by the TFM file, horizontal motions contribute their + * width, vertical motions are ignored, etc.) This procedure is + * called *simulating*; at its end we've just read an ER and we know + * the width of the segment between the BR and the ER. Call this + * width T. Note that nothing at all is output during simulation. + * + * Now we move right (in the output file) by T; of course we do so by + * shipping out a RIGHTn command. Next, we make a second pass over the + * commands between the BR and the ER; this time, we typeset them + * "backwards." For example, to SET an "a" we first move left by the + * width of an "a", and lay down the "a" without moving---this is the + * inverse of laying down the "a" and then moving right. If the command + * is any sort of horizontal motion we negate the distance before moving. + * Reflection applies only to horizontal motion, however; vertical + * motion commands, font changes, font definitions, and so forth, are + * copied unchanged. (For the precise way in which we reverse, see the + * documentation for the individual DVI commands below, especially + * /SetChar/ and /SetString/.) + * + * When we see the ER for the second time, the horizontal location in + * the output file should be the same as it was when we first saw the BR + * (since we've processed all the same commands, only backwards). So we + * once again output a command to move right by T, thus moving to the + * place where we typeset the first command after the BR. We're now + * done translating this reflected segment and we continue normally. + * + * What happens when reflections are nested? Well, it's easy. If we + * encounter a BR during a simulation, we basically ignore it! We're + * trying to find the total width of the reflected segment; the fact + * that part of the segment is further reflected doesn't matter. (We do + * have to be certain to skip over the inner ER so that we match only + * the ER we're looking for.) Later, when we're no longer simulating + * but "typesetting backwards," we'll encounter the BR of the inner + * reflection. At that point it gets its own simulation set up, and we + * do the whole two-pass business again on the inner segment; of course, + * the second pass is now a "forwards" pass, at the end of which we + * continue the "backwards" pass over the outer segment. + * + * All of this can be nested to any depth. We keep track of what we're + * doing with the variable /State/ which takes on the following values: + * LTYPESETTING Typesetting normally, left-to-right + * RTYPESETTING Typesetting "backwards," right-to-left + * SIMULATING First pass over text to be reflected + * >SIMULATING Inside nested begin_reflect/end_reflect pairs + * Values of /State/ greater than SIMULATING are used to keep track of + * the nesting depth of BR/ER pairs. While we're simulating, we simply + * increment /State/ when we see BR and decrement it when we see ER. + * We switch from simulating to typesetting only when we see ER while + * /State/ is exactly equal to SIMULATING. Note carefully that we're + * never simulating on behalf of more than one BR/ER pair; there's never + * more than one segment whose width we're measuring. + * + * So the procedure upon hitting a BR in the general case is as follows: + * if /State/ equals or exceeds SIMULATING, just increment /State/. + * Otherwise set /State/ to SIMULATING and start the first pass. The + * actions necessary at ER are a bit more complex: if /State/ is + * *greater* than SIMULATING, just decrement it. If /State/ is equal to + * SIMULATING, we've just finished a simulation: output the motion + * command as above, switch to the direction opposite to the one we were + * in when we hit the BR, and reset the input file to the point just + * after the BR, thus beginning the second pass. If /State/ is + * LTYPESETTING or RTYPESETTING, we've just finished the second pass + * over a reflection: invert /State/, and output the second long motion + * command as described above. Of course, there's lots of things to + * save and restore here; details are in the code below. + * + * Even when we're not simulating we have to keep track of certain + * values. The current font is an obvious example: we have to remember + * what it is, because if we start a simulation we'll have to measure + * its characters. We also keep track of the horizontal motion + * parameters: if a W0 appears in reflected text we must know the + * current value of W, which may have been set before the simulation + * began. Therefore we keep variables in which we store the values, and + * a stack to model all pushes and pops in the input file. The same + * stack is used to store values over a simulation; no conflict can + * occur. On the other hand, the values of the vertical motion + * parameters never concern us. + * + * One final twist. Suppose we encounter the following sequence: "BR + * <set W to d> a W0 b W0 c W0 ER" Now if we weren't clever, each W0 + * would have to be replaced by a longer RIGHTn command, because when + * we're typesetting backwards a W0 translates to a rightward motion by + * -d, not d. It's better to set W to -d to begin with; then we can use + * W0 inside the reflection and save considerable space. So we negate + * the parameter of Wn commands encountered while RTYPESETTING. But as + * a consequence, the input file may disagree with the output file over + * the current value of W. So we must keep two separate variables, + * /WInput/ and /WOutput/, that record the two values. These two + * variables always are equal in absolute value, but we can't compute + * from the state whether they're equal or not (because reflections may + * start and end independently of changes to W). More details are given + * at /SetWandMoveForward/. All of this applies to X as well. With + * more work we could do further optimization, catching (e.g.) cases + * like "<set W to d> BR a W0 b W0 ER" in which W0 will not be used as + * our scheme stands. + * + * Jacques Goldberg first suggested the possibility of a dvi-ivd to dvi + * processor. Please send comments, suggestions, and bug reports to + * larry@bbn.com or larry@harvard.edu. + * + */ + + +#include <stdio.h> +#include "global.h" +#include "commands.h" + +/* Global variables and procedures defined in auxiliary.c. */ +extern font *CurFont, *FindFont(); +extern void Initializations(), BadDVIAbort(), MaxPushLevelOutput(); +extern void PushWrite(), PopWrite(); +extern void PushWX(), PopXW(), PushDeltaH(), PopDeltaH(); +extern void FontDefine(), CopyFontDefinition(); + +/* Global variables and procedures defined in io.c. */ +extern unsigned BufSize, SignedBytes(); +extern long BytesOutput, CopyWord(); +extern long ReadSigned(), ReadUnsigned(), CharWidth(), CopyUnsigned(); +extern void WriteByte(), WriteString(), WriteNumber(), WriteWord(); +extern void CopyNBytes(), SkipNBytes(); +extern void RereadLastByte(), ResetFilePosition(); +extern unsigned_byte CopyByte(), ReadByte(), ReadCommand(); +extern unsigned_byte *ReadFilePosition(); + + +/* Global variables defined here */ +char *ProgramName; /* argv[0], used in error messages */ +boolean VerboseOutput = FALSE; /* v flag: report page progress */ +boolean ExactOutput = FALSE; /* X flag: try not to change input file */ +long PrevPagePointer = -1; /* output file location of previous BOP */ +int State; /* current direction or simulation depth*/ +long WInput, WOutput; /* value of W in input and output files */ +long XInput, XOutput; /* value of X in input and output files */ +long DeltaH; /* total size of reflected segment */ +int SavedState; /* state at start of current simulation */ +font *SavedFont; /* font at start of current simulation */ +unsigned_byte *SavedPosition; /* file position at start of simulation */ +unsigned_byte CurCommand; /* DVI command under consideration */ + +#define REVERSE(STATE) (LTYPESETTING + RTYPESETTING - STATE) + + +/* Procedures defined in this file, in order of definition */ +void main(),Arguments(),FileArgument(),Preliminaries(),MainLoop(); +void SetChar(),SetRule(),SetString(),SetFont(); +void BeginPage(),EndPage(),BegReflect(),EndReflect(); +void MoveForward(),SetWandMoveForward(),SetXandMoveForward(); +void Postliminaries(),CopyParametrizedCommand(); + + + + +/* + * Main procedure, whose function is self-documenting. The only way + * ivd2dvi can terminate normally is through the /exit/ here. + */ +void +main(ignore, argv) +int ignore; +char *argv[]; +{ + Arguments(argv); + Initializations(); + Preliminaries(); + MainLoop(); + Postliminaries(); + exit(0); +} + + + +/* + * Standard argument processing. Don't worry if a flag is given more + * than once, but allow at most one filename. We also allow forms like + * "-Xv", or even "-Xbv 1024" since the argument following any -b is + * taken as the new buffer size. + */ +void +Arguments(argv) +char *argv[]; +{ + char *arg; + boolean seenfile = FALSE; + int newbufsize; + + ProgramName = argv[0]; + for (arg = *++argv; arg; arg = *++argv) + if (*arg == '-') + while (*++arg) + switch(*arg) { + case 'X': + ExactOutput = TRUE; + break; + case 'v': + VerboseOutput = TRUE; + break; + case 'b': + if (!*++argv) { + fprintf(stderr, "%s: missing buffer size, -b ignored\n", + ProgramName); + return; + } else { + newbufsize = atoi(*argv); + if (newbufsize == 0) + fprintf(stderr, "%s: illegal buffer size %s ignored\n", + ProgramName, *argv); + else + BufSize = newbufsize; + } + break; + default: + fprintf(stderr, "%s: illegal flag %c ignored\n", + ProgramName, *arg); + } + else if (seenfile) + fprintf(stderr, "%s: superflous filename %s ignored\n", + ProgramName, arg); + else { + seenfile = TRUE; + FileArgument(arg); + } +} + + + +/* + * Process a file argument. Try to open the file. If we can't, and if + * the name has no period after its rightmost slash, append ".dvi" and + * try again. In either case we're reopening standard input, so that + * we can read the input DVI file from /stdin/ whether or not there's a + * filename on the command line. + */ +void +FileArgument(filename) +char *filename; +{ + char buf[MAXFILENAMESIZE], *p; + + if (freopen(filename, "r", stdin) != NULL) return; + p = rindex(filename, '/'); + if (*p == '\0') p = filename; + p = index(p, '.'); + if (*p != '\0') { + fprintf(stderr, "%s: Can't open %s\n", ProgramName, filename); + exit(1); + } else { + (void) sprintf(buf, "%s.dvi", filename); + if (freopen(buf, "r", stdin) != NULL) return; + fprintf(stderr, "%s: Can't open %s nor %s\n", + ProgramName, filename, buf); + exit(1); + } +} + + + +/* + * Process the preamble. We mostly just copy it through untouched, + * except that we check the first two bytes for correctness and update + * the comment string to say ivd2dvi was here. (Don't update the + * comment string if the -X flag was used nor when the new comment + * wouldn't fit.) + */ +void +Preliminaries() +{ + static char *comment = "; postprocessed by ivd2dvi"; + unsigned comlength; + + if (CopyByte() != PRE) BadDVIAbort("no preamble"); + if (CopyByte() != DVIVERSION) + BadDVIAbort("wrong DVI version in preamble"); + CopyNBytes(12L); + comlength = ReadByte(); + if (!ExactOutput && (comlength < 256 - strlen(comment))) { + WriteByte(comlength + strlen(comment)); + CopyNBytes((long) comlength); + WriteString(comment); + } else { + WriteByte(comlength); + CopyNBytes((long) comlength); + } +} + + + + +/* + * The main loop. Read a command, switch on it, and continue doing so + * forever. The only way out of this routine is when /CurCommand/ is + * POST. Most cases of the main switch are simply procedure calls; the + * rest are documented case-by-case. We first handle the most common + * case, for top speed: if /CurCommand/ is SET_000 through SET_127 and + * we're typesetting normally, we need do nothing but write it into the + * output. If we're simulating or typesetting backwards, /SetString/ + * will handle it. Other commands are handled inside the switch. Note: + * the first test relies on the assumption that SET_000 is 0, which we + * really shouldn't do, but then again it *is* the test for the most + * common case! Expressions like "CurCommand - W1 + 1" return the + * number of bytes in the first parameter of commands with four forms. + */ + +#define SETTING TRUE + +void +MainLoop() +{ + while (TRUE) { + + CurCommand = ReadCommand(); + + if (CurCommand <= SETC_127) { + if (State == LTYPESETTING) WriteByte(CurCommand); else SetString(); + continue; + } + + switch (CurCommand) { + + case SET1: case SET2: case SET3: case SET4: + SetChar(CurCommand - SET1 + 1, SETTING); + break; + + case PUT1: case PUT2: case PUT3: case PUT4: + SetChar(CurCommand - PUT1 + 1, !SETTING); + break; + + case SET_RULE: case PUT_RULE: + SetRule(); + break; + + case NOP: + if (ExactOutput) WriteByte(NOP); + break; + + case BOP: + BeginPage(); + break; + + case EOP: + EndPage(); + break; + + +/* + * When we see a PUSH/POP we save/restore the horizontal parameters. + * If we're simulating, we also have to save/restore /DeltaH/, since + * stuff that happens between the PUSH and the POP has no effect on + * the size of the reflected segment. (If we're not simulating, we + * don't care at all about /DeltaH/ and there's no sense saving it.) + * Note that the stack discipline works out as long as we restore + * in the opposite order that we save, since PUSH/POP must nest with + * respect to reflection---so we can't have a PUSH while simulating + * that matches a POP while not simulating. Oh yeah, I almost forgot: + * if we're *not* simulating, we must write the command to the output! + */ + case PUSH: + PushWX(); + if (State < SIMULATING) PushWrite(); else PushDeltaH(); + break; + + case POP: + if (State < SIMULATING) PopWrite(); else PopDeltaH(); + PopXW(); + break; + + case RIGHT1: case RIGHT2: case RIGHT3: case RIGHT4: + MoveForward(ReadSigned(CurCommand - RIGHT1 + 1)); + break; + + +/* + * Move forward by W. It's up to /MoveForward/ whether "forward" means + * left or right (or neither, if we're just simulating). The amount to + * move is of course the *input* file's idea of the current W. + */ + case W0: + MoveForward(WInput); + break; + + case W1: case W2: case W3: case W4: + SetWandMoveForward(ReadSigned(CurCommand - W1 + 1)); + break; + + +/* + * Move forward by X. Please see the comments for W0, above. + */ + case X0: + MoveForward(XInput); + break; + + case X1: case X2: case X3: case X4: + SetXandMoveForward(ReadSigned(CurCommand - X1 + 1)); + break; + + +/* + * We don't especially care about vertical motion, so if we're only + * simulating there's nothing to do. Otherwise, copy the command and + * its parameters to the output file. + */ + case DOWN1: case DOWN2: case DOWN3: case DOWN4: + if (State < SIMULATING) + CopyParametrizedCommand(CurCommand - DOWN1 + 1); + break; + + case Y0: case Y1: case Y2: case Y3: case Y4: + if (State < SIMULATING) CopyParametrizedCommand(CurCommand - Y0); + break; + + case Z0: case Z1: case Z2: case Z3: case Z4: + if (State < SIMULATING) CopyParametrizedCommand(CurCommand - Z0); + break; + + +/* + * We must keep track of all font changes, simulating or not. So call + * /SetFont/ on the selected font number, and if we're not simulating + * copy the whole command to the output file. + */ + case FNT1: case FNT2: case FNT3: case FNT4: + if (State >= SIMULATING) + SetFont(ReadUnsigned(CurCommand - FNT1 + 1)); + else { + WriteByte(CurCommand); + SetFont(CopyUnsigned(CurCommand - FNT1 + 1)); + } + break; + + +/* + * Skip past if simulating, otherwise copy out the whole command. + */ + case XXX1: case XXX2: case XXX3: case XXX4: + if (State >= SIMULATING) + SkipNBytes(ReadUnsigned(CurCommand - XXX1 + 1)); + else { + WriteByte(CurCommand); + CopyNBytes(CopyUnsigned(CurCommand - XXX1 + 1)); + } + break; + + case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: + FontDefine(CurCommand - FNT_DEF1 + 1); + break; + + case PRE: + BadDVIAbort("unexpected PRE"); + break; + + +/* + * Shouldn't see the postamble while simulating or reversing; otherwise + * it's time to quit. This is the only normal way out of /MainLoop/. + */ + case POST: + if (State != LTYPESETTING) BadDVIAbort("unexpected POST"); + return; + + case POST_POST: + BadDVIAbort("unexpected POST_POST"); + break; + + case BEG_REFLECT: + BegReflect(); + break; + + case END_REFLECT: + EndReflect(); + break; + + +/* + * The only commands left, besides illegal ones, are the one-byte font + * selection commands. Copy them to the output file unless simulating. + * Then call /SetFont/ to note the change of font. + */ + default: + if (CurCommand >= FONT_00 && CurCommand <= FONT_63) { + if (State < SIMULATING) WriteByte(CurCommand); + SetFont((long) CurCommand - FONT_00); + } else + BadDVIAbort("unrecognized command"); + + } /* end of "switch (CurCommand) { ... }" */ + } /* end of "while (TRUE) { ... }" */ +} /* end of procedure MainLoop */ + + + + +/* + * Set a single character as specified by a SETn or PUTn command. + * (SET_nnn commands don't come here; they go to /SetString/.) + * /setting/ tells whether it was a SET or a PUT, and /bytes/ gives the + * parameter length. If we're typesetting normally we just copy out the + * command and parameter, and if we're simulating it's enough to add the + * width of the desired character to the running total. If we're + * typesetting in reverse there's more to do: first move "forward" + * (left, in this case) by the width of the character. Only then do we + * typeset the character. Moreover, if the original command was a SET, + * we PUT the character since we've already moved. And if the original + * command was a PUT, we SET the character---which has the effect of + * moving back to the right, for a net motion of zero. + */ +void +SetChar(bytes, setting) +unsigned bytes; +boolean setting; +{ + long charnumber; + + switch (State) { + case LTYPESETTING: + CopyParametrizedCommand(bytes); + break; + case RTYPESETTING: + charnumber = ReadUnsigned(bytes); + MoveForward(CharWidth(charnumber)); + if (setting) + WriteByte(PUT1 + bytes - 1); + else + WriteByte(SET1 + bytes - 1); + WriteNumber(charnumber, bytes); + break; + default: /* State >= SIMULATING */ + DeltaH += CharWidth(ReadUnsigned(bytes)); + } +} + + + +/* + * Same idea as /SetChar/, just above. If LTYPESETTING just copy, if + * simulating just accumulate the width. If RTYPESETTING first move + * left and then interchange SET and PUT. + */ +void +SetRule() +{ + long height, width; + + switch (State) { + case LTYPESETTING: + CopyParametrizedCommand(8); + break; + case RTYPESETTING: + height = ReadSigned(4); + width = ReadSigned(4); + MoveForward(width); + if (CurCommand == SET_RULE) WriteByte(PUT_RULE); + else WriteByte(SET_RULE); + WriteWord(height); + WriteWord(width); + break; + default: /* State >= SIMULATING */ + SkipNBytes(4L); + width = ReadSigned(4); + if (CurCommand == SET_RULE) DeltaH += width; + } +} + + + +/* + * Handle a SET_nnn command. This routine is called only while + * simulating or typesetting in reverse (the normal case is handled + * directly in /MainLoop/ for speed). + * + * If we're just simulating there's nothing to do except add the width + * of the character to the running total. The RTYPESETTING case is the + * interesting one. Here's the story: We could typeset backwards + * as we do in /SetChar/ above: move left, then PUT (since all these + * commands are variants of SET). But that would mean that every one + * of these common one-byte commands would translate into around five + * bytes. So instead of handling a single command, this routine handles + * /CurCommand/ and all subsequent SET_nnn commands at once. We first + * continue to read the input until we hit a command that is *not* a + * SET_nnn. As we do so, we accumulate the total width (call it T) of + * all the SET_nnn commands that we've read. Finally, we write the + * following instructions into the output file: (a) move forward---that + * is, left---by T, (b) PUSH, to save the new value of H, (c) typeset + * all the characters we've seen *backwards*, (d) POP to get back to + * the place where we started emitting characters. This does the trick. + * + * A few notes: We must back up the input after we're done since that + * first non-SET_nnn command must be reconsidered by /MainLoop/. We + * rely on the fact that /ReadFilePosition/ gives us a pointer into a + * buffer from which we can rattle off /count/ characters; this is true + * since we must be rescanning input after a simulation. Finally, if + * there's only a single SET_nnn character to typeset we save a byte by + * replacing steps (b), (c) and (d) with a simple PUT1. + * + * This routine relies on the fact that SET_nnn in fact has value nnn, + * which, stylistically, is absolutely indefensible. + */ +void +SetString() +{ + unsigned_byte *p; + unsigned_byte nextcommand; + long totalwidth; + int count; + + if (State >= SIMULATING) + DeltaH += CharWidth((long) CurCommand); + else { /* State == RTYPESETTING */ + totalwidth = count = 0; + nextcommand = CurCommand; + do { + count++; + totalwidth += CharWidth((long) nextcommand); + nextcommand = ReadByte(); + } while (nextcommand <= SETC_127); + RereadLastByte(); + MoveForward(totalwidth); + if (count == 1) { + WriteByte(PUT1); + WriteByte(CurCommand); + } else { + PushWrite(); + p = ReadFilePosition(); + while (count--) WriteByte(*--p); + PopWrite(); + } + } +} + + + +/* + * Record the current font number, which must be done no matter what + * state we're in. The work is done by /FindFont/; we just put the + * result into /CurFont/ and complain if the font is unknown. + */ +void +SetFont(fontnumber) +long fontnumber; +{ + CurFont = FindFont(fontnumber); + if (CurFont == NULL) BadDVIAbort("font used but not defined"); +} + + + +/* + * Pages should end only in normal left-to-right mode. We write the + * BOP, copy the ten \count registers printing the first if in verbose + * mode, and write out the pointer to the previous page. (We can't copy + * the old one since page lengths may change.) We also update this + * pointer; it must point to the newly written BOP. Finally, we start + * out the font, W, and X registers correctly. + */ +void +BeginPage() +{ + long pagenumber; + + if (State != LTYPESETTING) BadDVIAbort("unexpected BOP"); + WriteByte(BOP); + pagenumber = CopyWord(); + if (VerboseOutput) fprintf(stderr, "[%ld", pagenumber); + CopyNBytes(36L); + SkipNBytes(4L); + WriteWord(PrevPagePointer); + PrevPagePointer = BytesOutput - 45; + WInput = WOutput = XInput = XOutput = 0; + CurFont = NULL; +} + + + +/* + * End a page. Must happen in "normal" LTYPESETTING mode, but otherwise + * there's nothing to do but write the EOP and chatter if requested. + */ +void +EndPage() +{ + static int pages = 0; + + if (State != LTYPESETTING) BadDVIAbort("unexpected EOP"); + WriteByte(EOP); + if (VerboseOutput) fprintf(stderr, "]%c", (++pages % 10) ? ' ' : '\n'); +} + + + +/* + * Start of a reflected segment, which may happen in any state. If + * we're already simulating just increment /State/ to indicate nested + * reflections (see the overview above). Otherwise start simulating: + * save the horizontal motion parameters, the current file location, the + * current font, and the current state. Start the running total of the + * size of this segment at zero. Change state to SIMULATING to start + * the first pass over the segment; the rest of the work is done at + * /EndReflect/. One subtlety: it's OK to save the font, the state, and + * the file position in variables since there's never more than one + * simulation at a time. We could do the same with the horizontal + * motion parameters, but we might as well use the extant stack. + */ +void +BegReflect() +{ + if (State >= SIMULATING) ++State; + else { + PushWX(); + SavedPosition = ReadFilePosition(); + SavedFont = CurFont; + SavedState = State; + State = SIMULATING; + DeltaH = 0; + } +} + + + + +/* + * Come here at the end of a reflected segment. We first comment the + * easy case, which happens to be coded last: If /State/ is greater + * than SIMULATING we're inside nested reflections that don't concern + * us yet, so just decrement /State/ to show that we're up a level. + * + * If /State/ says we're at the outermost simulation, we must wrap up + * the simulation and start the second pass. The direction will be the + * inverse of the direction just before the simulation started. We + * also reset the font and the horizontal parameters to their saved + * values, and set things up so we're rescanning the input just *after* + * the BEG_REFLECT that started the simulation. Finally, we move + * "forward" by the total width of the reflected segment; we'll now + * typeset everything "backward" from that point. (Note that we move + * "forward" by the *negative* of /DeltaH/; this is because we've + * already turned the state around. Exercise: why must the /PopXW/ + * precede the /MoveForward/?) But we're not quite done with /DeltaH/; + * we need it at the *end* of the second pass to move forward over + * the reflected segment again. Since the variable /DeltaH/ is used + * by any nested simulations, we must save its value on the stack. + * + * If /State/ says we're typesetting (either direction) then we've + * just finished the second pass over a reflected segment: grab the + * saved value of /DeltaH/, use it to move back over the reflected + * segment, then revert to the previous direction of typesetting + * (which is the opposite of the direction just finished). Be sure + * to see the overview if you don't understand this move by /DeltaH/. + * + * Harder exercise: When we've finished typesetting a simulation as + * just described, we repeat the move that we started with to get back + * to the right place to continue. Why don't we use PUSH and POP *in + * the output file* to remember the place? That is, why not add a call + * to /PushWrite/ just after /MoveForward(-DeltaH)/ in the SIMULATING + * case, and *replace* the /MoveForward/ in the typesetting cases with + * a call to /PopWrite/? We'd save a couple of bytes in the output, + * and wouldn't have to worry about the value of /DeltaH/ during the + * second pass (thus we could skip pushing and popping /DeltaH/). + * What's the disadvantage that vitiates this clever idea? + */ +void +EndReflect() +{ + switch(State) { + case SIMULATING: + CurFont = SavedFont; + ResetFilePosition(SavedPosition); + State = REVERSE(SavedState); /* must precede MoveForward */ + PopXW(); /* must precede MoveForward */ + MoveForward(-DeltaH); + PushDeltaH(); + break; + case LTYPESETTING: case RTYPESETTING: + PopDeltaH(); + MoveForward(-DeltaH); + State = REVERSE(State); + break; + default: /* State >SIMULATING */ + State--; + } +} + + + +/* + * Move "forward" by /distance/. If we're simulating it's enough to + * accumulate /distance/ into the running total, otherwise we need a + * motion command in the output file. Now "forward" means "right" if + * typesetting normally, but "left" if typesetting right-to-left, so + * first we negate distance if we're in the latter mode. If we can + * accomplish this move with a simple W0 or X0 we do so. Otherwise, + * we use a RIGHTn with the smallest possible n. Notice that we + * test /distance/ against the *output* file's idea of W and X when + * trying for the short versions. These may not agree with W and X + * in the input file; see the two routines immediately following. + */ +void +MoveForward(distance) +long distance; +{ + unsigned bytes; + + if (State >= SIMULATING) { DeltaH += distance; return; } + if (State == RTYPESETTING) distance = -distance; + if (distance == WOutput) WriteByte(W0); + else if (distance == XOutput) WriteByte(X0); + else { + bytes = SignedBytes(distance); + WriteByte(RIGHT1 - 1 + bytes); + WriteNumber(distance, bytes); + } +} + + + +/* + * Process a command in the range W1..W4. The parameter has been + * already been read. We always need to set this distance into WInput, + * the input file's idea of W. If we're just simulating, just + * accumulate /distance/ into the current total and quit (the value of + * WOutput is irrelevant during simulation). If typesetting left-to- + * right, output a Wn command to move and set W in the output file. If + * typesetting right-to-left, the same, except we first negate + * /distance/. This means two things: first of all, we'll move the + * opposite direction in the output file as desired. Secondly, the + * value of W in the output file (recorded in /WOutput/) is the negative + * of its value in the input file. That way, upcoming W0 commands will + * be directly usable as long as we're typesetting backwards. + */ +void +SetWandMoveForward(distance) +long distance; +{ + unsigned bytes; + + WInput = WOutput = distance; + if (State >= SIMULATING) { DeltaH += distance; return; } + if (State == RTYPESETTING) WOutput = distance = -distance; + bytes = SignedBytes(distance); + WriteByte(W1 - 1 + bytes); + WriteNumber(distance, bytes); +} + + + +/* + * Don't even think of finding comments for this routine. Just look up + * at /SetWandMoveForward/, OK? + */ +void +SetXandMoveForward(distance) +long distance; +{ + unsigned bytes; + + XInput = XOutput = distance; + if (State >= SIMULATING) { DeltaH += distance; return; } + if (State == RTYPESETTING) XOutput = distance = -distance; + bytes = SignedBytes(distance); + WriteByte(X1 - 1 + bytes); + WriteNumber(distance, bytes); +} + + + +/* + * Process the postamble, having just read a POST command from the input + * file. Mostly we just copy the postamble into the output file, with a + * few exceptions: The previous page pointers and maximum stack depth may + * be different, and NOP commands are suppressed unless the -X flag was + * used. When we hit POST_POST, we finish off the file with four 223's + * and enough more so that the total file length is divisible by four. + * Note that ivd2dvi stops reading its input file after seeing the dvi + * version character here. As a consequence, it's always an error for + * the input file to come to EOF. + */ +void +Postliminaries() +{ + long i; + + if (VerboseOutput) fprintf(stderr, "[post]\n"); + WriteByte(POST); + SkipNBytes(4L); + WriteWord(PrevPagePointer); + PrevPagePointer = BytesOutput - 5; + CopyNBytes(20L); + MaxPushLevelOutput(); + CopyNBytes(2L); + while (CurCommand != POST_POST) { + CurCommand = ReadByte(); + switch (CurCommand) { + case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: + CopyFontDefinition(ReadSigned(CurCommand - FNT_DEF1 + 1), + CurCommand - FNT_DEF1 + 1); + break; + case NOP: + if (ExactOutput) WriteByte(CurCommand); + break; + case POST_POST: + break; + default: + BadDVIAbort("unrecognized command in postamble"); + } + } + WriteByte(POST_POST); + SkipNBytes(4L); + WriteWord(PrevPagePointer); + if (CopyByte() != DVIVERSION) + BadDVIAbort("wrong DVI version in postamble"); + for (i = 7 - ((BytesOutput-1) % 4); i > 0; i--) WriteByte(DVIPADCHAR); +} + + + +/* + * Output the current command, then copy /bytes/ more bytes from the + * input file to the output file. Surprisingly useful. + */ +void +CopyParametrizedCommand(bytes) +unsigned bytes; +{ + WriteByte(CurCommand); + CopyNBytes((long) bytes); +} |