/* * 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 * 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 " 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 #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); }