dvi/
lib.rs

1//! # DVI file format
2//!
3//! This crate implements the DVI ("device-independent file format") format.
4//!
5//! The most important type in the crate is [`Op`],
6//! which describes a single operation or
7//! command in a DVI file. A DVI file is just a list of such operations.
8//!
9//! The crate provides an iterator,
10//! [`Deserializer`], that accepts raw DVI bytes and returns
11//! the operations in the DVI data.
12//! The inverse of this deserializer is the
13//! [`serialize()`] function.
14//!
15//! ## Knuth's description of the format
16//!
17//! The text in this section was written by Donald Knuth.
18//! It appears as documentation in TeX.2021.583.
19//!
20//! The most important output produced by a run of TeX is the "device
21//! independent" (DVI) file that specifies where characters and rules
22//! are to appear on printed pages. The form of these files was designed by
23//! David R. Fuchs in 1979. Almost any reasonable typesetting device can be
24//! driven by a program that takes DVI files as input, and dozens of such
25//! DVI-to-whatever programs have been written. Thus, it is possible to
26//! print the output of TeX on many different kinds of equipment, using TeX
27//! as a device-independent "front end."
28//!
29//! A DVI file is a stream of 8-bit bytes, which may be regarded as a
30//! series of commands in a machine-like language. The first byte of each command
31//! is the operation code, and this code is followed by zero or more bytes
32//! that provide parameters to the command. The parameters themselves may consist
33//! of several consecutive bytes; for example, the `set_rule` command
34//! ([`Op::TypesetRule`] with `move_h=true`) has two
35//! parameters, each of which is four bytes long. Parameters are usually
36//! regarded as nonnegative integers; but four-byte-long parameters,
37//! and shorter parameters that denote distances, can be
38//! either positive or negative. Such parameters are given in two's complement
39//! notation. For example, a two-byte-long distance parameter has a value between
40//! `-2^15` and `2^{15}-1`. As in TFM files, numbers that occupy
41//! more than one byte position appear in big endian order.
42//!
43//! A DVI file consists of a "preamble," followed by a sequence of one
44//! or more "pages," followed by a "postamble." The preamble is simply a
45//! command ([`Op::Preamble`] command, with its parameters that define the dimensions used in the
46//! file; this must come first.  Each "page" consists of a
47//! [`Op::BeginPage`] command,
48//! followed by any number of other commands that tell where characters are to
49//! be placed on a physical page, followed by an
50//! ([`Op::EndPage`] command. The pages
51//! appear in the order that TeX generated them. If we ignore
52//! [`Op::NoOp`] commands
53//! and [`Op::DefineFont`] command, which are allowed between any two commands in
54//! the file),
55//! each [`Op::BeginPage`] command is immediately followed by a [`Op::EndPage`] command,
56//! or by a [`Op::BeginPostamble`] command; in the latter case, there are no more pages in the
57//! file, and the remaining bytes form the postamble.
58//! Further details about
59//! the postamble will be explained later.
60//!
61//! Some parameters in DVI commands are "pointers." These are four-byte
62//! quantities that give the location number of some other byte in the file;
63//! the first byte is number~0, then comes number~1, and so on. For example,
64//! one of the parameters of a `bop` command points to the previous `bop`;
65//! this makes it feasible to read the pages in backwards order, in case the
66//! results are being directed to a device that stacks its output face up.
67//! Suppose the preamble of a DVI file occupies bytes 0 to 99. Now if the
68//! first page occupies bytes 100 to 999, say, and if the second
69//! page occupies bytes 1000 to 1999, then the `bop` that starts in byte 1000
70//! points to 100 and the `bop` that starts in byte 2000 points to 1000. (The
71//! very first `bop`, i.e., the one starting in byte 100, has a pointer of -1.)
72
73mod deserialize;
74mod serialize;
75pub mod transforms;
76
77/// A variable in DVI data.
78///
79/// DVI data has access to four variables.
80/// These variables are set using the [`Op::SetVar`] operation
81/// and used in the [`Op::Move`] operation.
82///
83/// These variables and associated operations exist to support
84/// the following optimization.
85/// It is possible to replace repeated
86/// identical [`Op::Right`]/[`Op::Down`] operations with sets and moves
87/// that serialize to a smaller number of bytes.
88/// For example this DVI sequence:
89/// ```
90/// vec![
91///     dvi::Op::Down(300),
92///     dvi::Op::Down(300),
93///     dvi::Op::Down(300),
94/// ];
95/// ```
96/// is identical to this DVI sequence:
97/// ```
98/// vec![
99///     // Set Y to 300 and move down by that value.
100///     dvi::Op::SetVar(dvi::Var::Y, 300),
101///     // Move down by the current value of Y, 300.
102///     dvi::Op::Move(dvi::Var::Y),
103///     dvi::Op::Move(dvi::Var::Y),
104/// ];
105/// ```
106/// The first sequence serializes to 9 bytes,
107/// while the second serializes to 5 bytes.
108/// This optimization is performed in TeX.2021.595 and onwards.
109///
110/// Of the four variables, [`Var::W`] and [`Var::X`] operate on the
111/// horizontal part of the cursor _h_, and [`Var::Y`] and [`Var::Z`]
112/// operate on the vertical part of the cursor _v_.
113#[derive(Debug, Copy, Clone, PartialEq, Eq)]
114pub enum Var {
115    W = 0,
116    X = 1,
117    Y = 2,
118    Z = 3,
119}
120
121/// Operation that appears in DVI data.
122///
123/// The documentation for each variant is adapted from TeX.2021.585.
124/// However the variants don't map one-to-one on to commands as described
125/// there. Instead, commands that are logically connected are represented
126/// in the same variant. For example, `set_char_0`, `set1` and `put1` are
127/// all represented using the [`Op::TypesetChar`] variant.
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub enum Op {
130    /// Typeset the specified character from the current font _f_
131    /// such that the reference point of the character is at (_h_,_v_).
132    ///
133    /// This op corresponds to the DVI commands `set_char_N`, `set_N`
134    /// and `put_N`.
135    TypesetChar {
136        /// The character to typeset.
137        char: u32,
138        /// If true, after typesetting the character,
139        /// increase _h_ by the width of that character. Note that a character may
140        /// have zero or negative width, so one cannot be sure that _h_ will advance
141        /// after this command; but _h_ usually does increase.
142        ///
143        /// This field is true for `setX` commands and false for `putX` commands.
144        move_h: bool,
145    },
146    /// Typeset a solid black rectangle
147    /// of the provided height and width, with its bottom left corner at (_h_,_v_).
148    ///
149    /// If either the width or height is not positive, nothing should be typeset.
150    ///
151    /// This op corresponds to the DVI commands `set_rule` and `put_rule`.
152    ///
153    /// [TeX.2021.589]
154    /// Sometimes it is desirable to make horizontal or vertical rules line up
155    /// precisely with certain features in characters of a font. It is possible to
156    /// guarantee the correct matching between DVI output and the characters
157    /// generated by MetaFont by adhering to the following principles:
158    ///
159    /// 1. The MetaFont
160    ///    characters should be positioned so that a bottom edge or left edge that is
161    ///    supposed to line up with the bottom or left edge of a rule appears at the
162    ///    reference point, i.e., in row 0 and column 0 of the MetaFont raster. This
163    ///    ensures that the position of the rule will not be rounded differently when
164    ///    the pixel size is not a perfect multiple of the units of measurement in
165    ///    the DVI file.
166    ///
167    /// 1. A typeset rule of positive height and positive width
168    ///    should be equivalent to a MetaFont-generated character having black pixels in
169    ///    precisely those raster positions whose MeatFont coordinates satisfy
170    ///    0≤_x_<_wa_ and 0≤_y_<_ha_, where _a_ is the number
171    ///    of pixels per DVI unit.
172    TypesetRule {
173        /// Height of the rule.
174        height: i32,
175        /// Width of the rule.
176        width: i32,
177        /// If true, after typesetting the rule,
178        /// increase _h_ by the width of the rule.
179        ///
180        /// Note
181        /// that if the width is negative, the value of _h_
182        /// will decrease even though the rule is not typeset.
183        ///
184        /// This field is true for `set_rule` commands and false for `put_rule` commands.
185        move_h: bool,
186    },
187    /// No operation, do nothing.
188    ///
189    /// This op corresponds to the DVI commands `nop`.
190    NoOp,
191    /// Beginning of a page.
192    ///
193    /// Set (_h_,_v_,_w_,_x_,_y_,_z_) equal to (0,0,0,0,0,0) and set the stack empty.
194    /// Set the current font _f_ to an undefined value.
195    ///
196    /// This op corresponds to the DVI commands `bop`.
197    BeginPage {
198        /// The ten parameters.
199        ///
200        /// In the output from TeX, these hold
201        /// the values of `\count 0`...`\count 9`
202        /// at the time shipout was invoked for this page.
203        /// They can be used to identify pages,
204        /// if a user wants to print only part of a DVI file.
205        parameters: [i32; 10],
206        /// Pointer to the previous [`Op::BeginPage`] in the file,
207        /// or -1 if this is the first begin page op.
208        previous_begin_page: i32,
209    },
210    /// End of page: Print what you have read since the
211    /// previous [`Op::BeginPage`].
212    /// At this point the stack should be empty.
213    ///
214    /// The DVI-reading
215    /// programs that drive most output devices will have kept a buffer of the
216    /// material that appears on the page that has just ended. This material is
217    /// largely, but not entirely, in order by _v_ coordinate and (for fixed _v_) by
218    /// _h_ coordinate; so it usually needs to be sorted into some order that is
219    /// appropriate for the device in question.
220    ///
221    /// This op corresponds to the DVI commands `eop`.
222    EndPage,
223    /// Push the current values of
224    /// (_h_,_v_,_w_,_x_,_y_,_z_)
225    ///  onto the
226    /// top of the stack; do not change any of these values. Note that _f_ is
227    /// not pushed.
228    ///
229    /// This op corresponds to the DVI commands `push`.
230    Push,
231
232    /// Pop the top six values off of the stack and assign
233    /// them respectively to
234    /// (_h_,_v_,_w_,_x_,_y_,_z_).
235    /// The number of pops should never
236    /// exceed the number of pushes, since it would be highly embarrassing if the
237    /// stack were empty at the time of a pop command.
238    ///
239    /// This op corresponds to the DVI commands `pop`.
240    Pop,
241    /// Move _h_ right by the number in the payload.
242    /// If the payload is negative, _h_ moves left.
243    ///
244    /// This op corresponds to the four DVI commands `rightN`.
245    Right(i32),
246    /// Move _h_ or _v_ by the value of the variable in the payload.
247    ///
248    /// If the variable is _w_ or _x_, _h_ is moved.
249    /// If the variable is _y_ or _z_, _v_ is moved.
250    ///
251    /// With luck,
252    /// this parameterless command will usually suffice for moving _h_ and _v_,
253    /// because the same kind of motion
254    /// will occur several times in succession.
255    ///
256    /// This op corresponds to the four DVI commands `w0`, `x0`, `y0` and `z0`.
257    Move(Var),
258    /// Set the value of the specified variable, and then move _h_ or _v_
259    /// based on the new value.
260    ///
261    /// If the variable is _w_ or _x_, _h_ is moved.
262    /// If the variable is _y_ or _z_, _v_ is moved.
263    ///
264    /// This op corresponds to the four DVI commands `wN`, `xN`, `yN` and `zN`
265    /// for `N>0`.
266    SetVar(Var, i32),
267    /// Move _v_ down by the number in the payload.
268    /// If the payload is negative, _v_ moves up.
269    ///
270    /// This op corresponds to the four DVI commands `downN`.
271    Down(i32),
272    /// Enable the specified font.
273    /// This font must have been previously defined by a [`Op::DefineFont`]
274    /// command.
275    ///
276    /// This op corresponds to the DVI commands `fnt_num_N` and `fntN`.
277    EnableFont(u32),
278
279    /// This command is undefined in
280    /// general; it functions as a (_k_+2)-byte [`Op::NoOp`],
281    /// where _k_ is the number of bytes,
282    ///  unless special DVI-reading
283    /// programs are being used.
284    ///
285    /// It
286    /// is recommended that the payload be a string having the form of a keyword followed
287    /// by possible parameters relevant to that keyword.
288    ///
289    /// This op corresponds to the DVI commands `xxxN`.
290    Extension(Vec<u8>),
291    /// Define a font.
292    ///
293    /// This op corresponds to the DVI commands `fnt_defN`.
294    ///
295    /// The rest of the documentation on this variant comes from TeX.2021.588.
296    /// Font definitions must appear before the first use of a particular font number.
297    /// Once a font is defined with a specific number, it must not be defined again; however, we
298    /// definitions appear in the postamble as well as
299    /// in the pages, so in this sense each font number is defined exactly twice,
300    /// if at all. Like [`Op::NoOp`] commands, font definitions can
301    /// appear before the first [`Op::BeginPage`],
302    /// or between an [`Op::EndPage`] and a [`Op::BeginPage`].
303    DefineFont {
304        /// Number of the font.
305        number: u32,
306        /// Check sum that TeX found in the TFM
307        /// file for this font; this should match the check sum of the font found by
308        /// programs that read this DVI file.
309        checksum: u32,
310        /// A fixed-point scale factor that is applied to
311        /// the character widths this font; font dimensions in TFM files and
312        /// other font files are relative to this quantity, which is called the
313        /// "at size" elsewhere in this documentation. The value of the parameter is
314        /// always positive and less than `2^27`. It is given in the same units
315        /// as the other DVI dimensions, i.e., in sp when TeX has made the
316        /// file.  
317        at_size: u32,
318        /// Similar to the at size; it is the "design size," and
319        /// like the at size it is given in DVI units. Thus, this font is to be used
320        /// at _ms_/(1000 _d_) times its normal size, where _m_ is the magnification,
321        /// _s_ is the at size and _d_ is the design size.
322        design_size: u32,
323        /// The "area" or directory of the font.
324        /// The standard local system font area is supposed to be used this is empty.
325        area: String,
326        /// The external name of the font.
327        name: String,
328    },
329    /// The preamble.
330    /// This must come at the very beginning of the file.
331    ///
332    /// This op corresponds to the DVI command `pre`.
333    ///
334    /// The rest of the documentation on this variant comes from TeX.2021.587.
335    /// The preamble contains basic information about the file as a whole.
336    Preamble {
337        /// The DVI format; currently this byte is always set
338        /// to 2. (The value 3 is currently used for an extended format that
339        /// allows a mixture of right-to-left and left-to-right typesetting.
340        /// Some day we will set the format to 4, when DVI format makes another
341        /// incompatible change---perhaps in the year 2048.
342        dvi_format: u8,
343        /// The next two parameters are positive integers that define
344        /// the units of measurement; they are the numerator and denominator of a
345        /// fraction by which all dimensions in the DVI file could be multiplied
346        /// in order to get lengths in units of `10^(-7)` meters. Since 7227pt =
347        /// 254cm, and since TeX works with scaled points where there are `2^16`
348        /// sp in a point, TeX sets
349        /// the numerator to `254x10^5=25400000`
350        /// and the denominator to
351        /// `7227x2^16=473628672`.
352        unit_numerator: u32,
353        /// See the description of the previous field.
354        unit_denominator: u32,
355        /// The magnification parameter is what TeX calls `\mag`, i.e., 1000 times the
356        /// desired magnification. The actual fraction by which dimensions are
357        /// multiplied is therefore _mn_/(1000 _d_).
358        /// Note that if a TeX
359        /// source document does not call for any "true" dimensions, and if you
360        /// change it only by specifying a different `\mag` setting, the DVI
361        /// file that TeX creates will be completely unchanged except for the value
362        /// of the maginification in the preamble and postamble.
363        /// (Fancy DVI-reading programs allow
364        /// users to override the magnificiation setting when a DVI file is being printed.)
365        magnification: u32,
366        /// A comment, which is not interpreted further.
367        comment: String,
368    },
369    /// The start of the postamble.
370    ///
371    /// This op corresponds to the DVI command `post`.
372    ///
373    /// The rest of the documentation on this variant comes from TeX.2021.590.
374    ///
375    ///
376    /// This command
377    /// introduces the postamble, which summarizes important facts that TeX has
378    /// accumulated about the file, making it possible to print subsets of the data
379    /// with reasonable efficiency. The postamble has the form:
380    ///
381    /// - [`Op::BeginPostamble`].
382    /// - Multiple font definitions ([`Op::DefineFont`]).
383    /// - [`Op::EndPostamble`].
384    BeginPostamble {
385        /// A pointer to the final [`Op::BeginPage`] in the file.
386        final_begin_page: i32,
387        /// Duplicate of the analagous parameter in [`Op::Preamble`]
388        unit_numerator: u32,
389        /// Duplicate of the analagous parameter in [`Op::Preamble`]
390        unit_denominator: u32,
391        /// Duplicate of the analagous parameter in [`Op::Preamble`]
392        magnification: u32,
393        /// The height-plus-depth of the tallest
394        /// page, in the same units as other dimensions
395        /// of the file. This height, along with the next width parameter,
396        /// might be used by a DVI-reading program to
397        /// position individual "pages" on large sheets of film or paper; however,
398        /// the standard convention for output on normal size paper is to position each
399        /// page so that the upper left-hand corner is exactly one inch from the left
400        /// and the top. Experience has shown that it is unwise to design DVI-to-printer
401        /// software that attempts cleverly to center the output; a fixed position of
402        /// the upper left corner is easiest for users to understand and to work with.
403        /// Therefore this field and the next field are are often ignored.
404        largest_height: u32,
405        /// The width of the widest page in the same units as other dimensions
406        /// of the file.
407        largest_width: u32,
408        /// The maximum stack depth (i.e., the largest excess of
409        /// [`Op::Push`] commands over [`Op::Pop`] commands) needed to process this file.
410        max_stack_depth: u16,
411        /// The total number of pages ([`Op::BeginPage`] commands) present.
412        num_pages: u16,
413    },
414    /// The end of the postamble.
415    ///
416    /// This op corresponds to the DVI command `post_post`.
417    ///
418    /// The rest of the documentation on this variant comes from TeX.2021.591.
419    EndPostamble {
420        /// A pointer to the
421        /// [`Op::BeginPostamble`] command that started the postamble.
422        postamble: i32,
423        /// Duplicate of the analagous parameter in [`Op::Preamble`]
424        dvi_format: u8,
425        /// The DVI format byte is followed by four or more bytes that are all equal to
426        /// the decimal number 223 (i.e., 0x337). TeX puts out four to seven of
427        /// these trailing bytes, until the total length of the file is a multiple of
428        /// four bytes, since this works out best on machines that pack four bytes per
429        /// word; but any number of 223's is allowed, as long as there are at least four
430        /// of them. In effect, 223 is a sort of signature that is added at the very end.
431        ///
432        /// This curious way to finish off a DVI file makes it feasible for
433        /// DVI-reading programs to find the postamble first, on most computers,
434        /// even though TeX wants to write the postamble last. Most operating
435        /// systems permit random access to individual words or bytes of a file, so
436        /// the DVI reader can start at the end and skip backwards over the 223's
437        /// until finding the DVI identification byte. Then it can back up four bytes, read
438        /// the postamble pointer, and move to that byte of the file. This byte should, of course,
439        /// contain the value 248 (the op code for [`Op::BeginPostamble`]);
440        /// now the postamble can be read, so the
441        /// DVI reader can discover all the information needed for typesetting the
442        /// pages. Note that it is also possible to skip through the DVI file at
443        /// reasonably high speed to locate a particular page, if that proves
444        /// desirable. This saves a lot of time, since DVI files used in production
445        /// jobs tend to be large.
446        ///
447        /// Unfortunately, however, standard Pascal does not include the ability to
448        /// access a random position in a file, or even to determine the length of a file.
449        ///  Almost all systems nowadays provide the necessary capabilities, so DVI
450        ///  format has been designed to work most efficiently with modern operating systems.
451        ///   But if DVI files have to be processed under the restrictions of standard
452        ///  Pascal, one can simply read them from front to back, since the necessary
453        ///   header information is present in the preamble and in the font definitions.
454        num_223_bytes: usize,
455    },
456}
457
458impl Op {
459    /// Deserialize the next operation from the provided binary slice.
460    ///
461    /// Note that in general it is easier to perform deserialization using the
462    /// [`Deserializer`] iterator.
463    ///
464    /// There are three possible return values from this method.
465    /// If the deserialization succeeds, the return value contains the operation
466    /// and the tail of the slice that was not consumed.
467    /// This tail can be used to deserialize the next operation:
468    ///
469    /// ```
470    /// let data = vec![128, 4, 129, 1, 0];
471    /// let (op, tail) = dvi::Op::deserialize(&data).unwrap().unwrap();
472    /// assert_eq![op, dvi::Op::TypesetChar{char: 4, move_h: true}];
473    /// assert_eq![tail, &[129, 1, 0]];
474    ///
475    /// let (op, tail) = dvi::Op::deserialize(&tail).unwrap().unwrap();
476    /// assert_eq![op, dvi::Op::TypesetChar{char: 256, move_h: true}];
477    /// assert_eq![tail, &[]];
478    /// ```
479    ///
480    /// If the slice is exhausted, the method returns [`None`]:
481    ///
482    /// ```
483    /// let data = vec![];
484    /// assert_eq![dvi::Op::deserialize(&data), Ok(None)];
485    /// ```
486    ///
487    /// If the data is not valid DVI data, an error is returned:
488    /// ```
489    /// let data_1 = vec![254];
490    /// assert_eq![
491    ///     dvi::Op::deserialize(&data_1),
492    ///     Err(dvi::InvalidDviData::InvalidOpCode(254)),
493    /// ];
494    ///
495    /// let data_2 = vec![129, 1];
496    /// assert_eq![
497    ///     dvi::Op::deserialize(&data_2),
498    ///     Err(dvi::InvalidDviData::Truncated(129)),
499    /// ];
500    /// ```
501    pub fn deserialize(b: &[u8]) -> Result<Option<(Self, &[u8])>, InvalidDviData> {
502        deserialize::deserialize(b)
503    }
504
505    /// Serialize this operation to bytes and append them to the provided vector.
506    ///
507    /// Unless you want close control over allocations, it's likely easier
508    /// to use the top-level [`serialize()`] function.
509    ///
510    /// ```
511    /// let mut data = vec![];
512    /// let op = dvi::Op::Right(256);
513    /// op.serialize(&mut data);
514    /// assert_eq![data, vec![144, 1, 0]];
515    /// ```
516    pub fn serialize(&self, b: &mut Vec<u8>) {
517        serialize::serialize(self, b)
518    }
519}
520
521/// Error returned if deserializing DVI data fails.
522///
523/// This error is returned from the [`Op::deserialize`] method
524/// and the [`Deserializer`] iterator.
525#[derive(Clone, Debug, PartialEq, PartialOrd)]
526pub enum InvalidDviData {
527    /// An invalid op code appeared.
528    InvalidOpCode(u8),
529    /// The file ended while parsing the payload of an operation.
530    /// The op code of the operation is provided.
531    Truncated(u8),
532}
533
534impl std::error::Error for InvalidDviData {}
535
536impl std::fmt::Display for InvalidDviData {
537    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
538        match self {
539            InvalidDviData::InvalidOpCode(op_code) => {
540                write!(f, "invalid op code {op_code}")
541            }
542            InvalidDviData::Truncated(op_code) => {
543                write!(f, "data ended while parsing payload for op code {op_code}")
544            }
545        }
546    }
547}
548
549/// Iterator that deserializes bytes into [`Op`] values.
550///
551/// ```
552/// let data: Vec<u8> = vec![158, 1, 0, 68, 86, 73];
553/// let mut result = Ok(());
554/// let ops: Vec<dvi::Op> = dvi::Deserializer::new(&data, &mut result).collect();
555/// assert_eq![result, Ok(())];
556/// assert_eq![
557///     ops,
558///     vec![
559///         dvi::Op::Down(256),
560///         dvi::Op::TypesetChar{char: 'D' as u32, move_h: true},
561///         dvi::Op::TypesetChar{char: 'V' as u32, move_h: true},
562///         dvi::Op::TypesetChar{char: 'I' as u32, move_h: true},
563///     ],
564/// ];
565/// ```
566///
567/// The deserializer returns [`Op`] values so that it can easily compose with other
568/// iterators.
569/// However the DVI data can be invalid.
570/// This error is reported through a side channel result value:
571///
572/// ```
573/// let invalid_data: Vec<u8> = vec![158, 1, 0, 255];
574/// let mut result = Ok(());
575/// let ops: Vec<dvi::Op> = dvi::Deserializer::new(&invalid_data, &mut result).collect();
576/// assert_eq![result, Err(dvi::InvalidDviData::InvalidOpCode(255))];
577/// assert_eq![
578///     ops,
579///     // Ops in the file before the error is hit are returned in the iterator.
580///     vec![dvi::Op::Down(256)],
581/// ];
582/// ```
583pub struct Deserializer<'a> {
584    b: &'a [u8],
585    result: &'a mut Result<(), InvalidDviData>,
586}
587
588impl<'a> Deserializer<'a> {
589    /// Create a new iterator from the provided binary slice.
590    pub fn new(b: &'a [u8], result: &'a mut Result<(), InvalidDviData>) -> Self {
591        Self { b, result }
592    }
593}
594
595impl<'a> Iterator for Deserializer<'a> {
596    type Item = Op;
597
598    fn next(&mut self) -> Option<Self::Item> {
599        match Op::deserialize(self.b) {
600            Ok(None) => None,
601            Ok(Some((op, b))) => {
602                self.b = b;
603                Some(op)
604            }
605            Err(err) => {
606                *self.result = Err(err);
607                None
608            }
609        }
610    }
611}
612
613/// Serialize operations to DVI data bytes.
614///
615/// ```
616/// let ops = vec![
617///     dvi::Op::Down(256),
618///     dvi::Op::TypesetChar{char: 'D' as u32, move_h: true},
619///     dvi::Op::TypesetChar{char: 'V' as u32, move_h: true},
620///     dvi::Op::TypesetChar{char: 'I' as u32, move_h: true},
621/// ];
622/// let data = dvi::serialize(ops);
623/// assert_eq!(data, vec![158, 1, 0, 68, 86, 73]);
624/// ```
625pub fn serialize<I: IntoIterator<Item = Op>>(i: I) -> Vec<u8> {
626    let mut v = vec![];
627    for op in i {
628        op.serialize(&mut v);
629    }
630    v
631}
632
633/// Data structure for tracking values in DVI data.
634///
635/// The DVI format refers to seven runtime values that are modified based
636/// on operations in the data.
637/// These seven values are:
638///
639/// - The current font, _f_.
640///
641/// - The two coordinates of the current cursor position, (_h_,_v_).
642///
643/// - The four values of the four variables
644///   [`Var::W`],
645///   [`Var::X`],
646///   [`Var::Y`],
647///   [`Var::Z`].
648///
649/// This data structure provides a mechanism for calculating these values
650/// as DVI operations are run:
651///
652/// ```
653/// let mut values: dvi::Values = Default::default();
654/// assert_eq!(values.y(), 0);
655///
656/// values.update(&dvi::Op::SetVar(dvi::Var::Y, 3));
657/// assert_eq!(values.y(), 3);
658///
659/// values.update(&dvi::Op::Push);
660/// values.update(&dvi::Op::SetVar(dvi::Var::Y, 5));
661/// assert_eq!(values.y(), 5);
662///
663/// values.update(&dvi::Op::Pop);
664/// assert_eq!(values.y(), 3);
665/// ```
666///
667/// Six of the values are just integers, which are simple to deal with.
668/// The value of _h_ is more complicated;
669/// see the documentation on [`Values::h`] for more information.
670///
671/// ## Knuth's description of the values
672///
673/// This text is from TeX.2021.584.
674///
675/// The DVI format is intended to be both compact and easily interpreted
676/// by a machine. Compactness is achieved by making most of the information
677/// implicit instead of explicit. When a DVI-reading program reads the
678/// commands for a page, it keeps track of several quantities:
679///
680/// 1. The current font _f_ is an integer; this value is changed only
681///    by `fnt` and `fnt_num` commands
682///    (both commands are represented by [`Op::EnableFont`]).
683///
684/// 2. The current position on the page
685///    is given by two numbers called the horizontal and vertical coordinates,
686///    _h_ and _v_. Both coordinates are zero at the upper left corner of the page;
687///    moving to the right corresponds to increasing the horizontal coordinate, and
688///    moving down corresponds to increasing the vertical coordinate. Thus, the
689///    coordinates are essentially Cartesian, except that vertical directions are
690///    flipped; the Cartesian version of (_h_,_v_) would be (_h_,_-v_).
691///
692/// 3. The current spacing amounts are given by four numbers _w_, _x_, _y_, and _z_,
693///    where _w_ and _x_ are used for horizontal spacing and where _y_ and _z_
694///    are used for vertical spacing.
695///
696/// 4. There is a stack containing
697///    (_h_,_v_,_w_,_x_,_y_,_z_) values; the DVI commands `push`
698///    ([`Op::Push`]) and `pop` ([`Op::Pop`]) are used to
699///    change the current level of operation. Note that the current font _f_ is
700///    not pushed and popped; the stack contains only information about
701///    positioning.
702///
703/// The values of _h_, _v_, _w_, _x_, _y_, and _z_ are signed integers having up
704/// to 32 bits, including the sign. Since they represent physical distances,
705/// there is a small unit of measurement such that increasing _h_ by 1 means
706/// moving a certain tiny distance to the right. The actual unit of
707/// measurement is variable, as explained below; TeX sets things up so that
708/// its DVI output is in sp units, i.e., scaled points, in agreement with
709/// all the scaled dimensions in TeX's data structures.
710#[derive(Default)]
711pub struct Values {
712    f: u32,
713    top: StackValues,
714    tail: Vec<StackValues>,
715}
716
717#[derive(Default, Clone, PartialEq, Eq)]
718struct StackValues {
719    h: i32,
720    h_chars: Vec<(u32, u32)>,
721    v: i32,
722    vars: [i32; 4],
723}
724
725impl Values {
726    /// Update the values by applying the provided operation.
727    pub fn update(&mut self, op: &Op) -> bool {
728        match op {
729            Op::TypesetChar { char, move_h } => {
730                if *move_h {
731                    self.top.h_chars.push((*char, self.f()));
732                    true
733                } else {
734                    false
735                }
736            }
737            Op::TypesetRule {
738                height: _,
739                width,
740                move_h,
741            } => {
742                if *move_h {
743                    self.top.h += *width;
744                    *width != 0
745                } else {
746                    false
747                }
748            }
749            Op::NoOp => false,
750            Op::BeginPage {
751                parameters: _,
752                previous_begin_page: _,
753            } => {
754                self.tail = vec![];
755                let new: StackValues = Default::default();
756                let changed = self.top != new;
757                self.top = new;
758                changed
759            }
760            Op::EndPage => false,
761            Op::Push => {
762                self.tail.push(self.top.clone());
763                false
764            }
765            Op::Pop => {
766                let Some(top) = self.tail.pop() else {
767                    return false;
768                };
769                let changed = top != self.top;
770                self.top = top;
771                changed
772            }
773            Op::Right(d) => {
774                self.top.h += *d;
775                *d != 0
776            }
777            Op::Move(var) => {
778                let d = self.top.vars[*var as usize];
779                match var {
780                    Var::W | Var::X => {
781                        self.top.h += d;
782                    }
783                    Var::Y | Var::Z => {
784                        self.top.v += d;
785                    }
786                }
787                d != 0
788            }
789            Op::SetVar(var, i) => {
790                let old = self.top.vars[*var as usize];
791                self.top.vars[*var as usize] = *i;
792                match var {
793                    Var::W | Var::X => {
794                        self.top.h += *i;
795                    }
796                    Var::Y | Var::Z => {
797                        self.top.v += *i;
798                    }
799                }
800                // This is only a noop if the old and new values are both zero.
801                // In this case the variable assignment does nothing, and
802                // the position is also unchanged.
803                old != 0 || *i != 0
804            }
805            Op::Down(d) => {
806                self.top.v += *d;
807                *d != 0
808            }
809            Op::EnableFont(f) => {
810                let old = self.f;
811                self.f = *f;
812                old != *f
813            }
814            Op::Extension(_)
815            | Op::DefineFont { .. }
816            | Op::Preamble { .. }
817            | Op::BeginPostamble { .. }
818            | Op::EndPostamble { .. } => false,
819        }
820    }
821    /// Get the current value of the font, _f_.
822    ///
823    /// Note that unlike every other value, the font is not affected by
824    /// push and pop operations.
825    ///
826    /// ```
827    /// let mut values: dvi::Values = Default::default();
828    /// values.update(&dvi::Op::EnableFont(1));
829    /// assert_eq![values.f(), 1];
830    /// values.update(&dvi::Op::Push);
831    /// values.update(&dvi::Op::EnableFont(2));
832    /// assert_eq![values.f(), 2];
833    /// values.update(&dvi::Op::Pop);
834    /// assert_eq![values.f(), 2];
835    /// ```
836    pub fn f(&self) -> u32 {
837        self.f
838    }
839    /// Get the current value of the horizontal position, _h_.
840    ///
841    /// The value of _h_ is more complicated than other values.
842    /// The DVI format includes a `set_char` command that typesets a character
843    /// and then increases _h_ by the width of that character.
844    /// The problem is that without looking up the font metric file,
845    ///     the data structure doesn't know by how much to increase _h_.
846    /// Thus, the value of _h_ in this data structure is an integer
847    ///     plus a slice of characters whose widths should be added to
848    ///     get the true value of _h_.
849    ///
850    /// ```
851    /// let mut values: dvi::Values = Default::default();
852    /// // move h 3 units to the right
853    /// values.update(&dvi::Op::Right(1));
854    /// // set the font
855    /// values.update(&dvi::Op::EnableFont(2));
856    /// // typeset DVI and move h each time
857    /// values.update(&dvi::Op::TypesetChar{char: 'D' as u32, move_h: true});
858    /// values.update(&dvi::Op::TypesetChar{char: 'V' as u32, move_h: true});
859    /// values.update(&dvi::Op::TypesetChar{char: 'I' as u32, move_h: true});
860    ///
861    /// assert_eq![
862    ///     values.h(),
863    ///     (1_i32, [
864    ///         ('D' as u32, 2_u32),
865    ///         ('V' as u32, 2_u32),
866    ///         ('I' as u32, 2_u32),
867    ///     ].as_slice()),
868    /// ];
869    /// ```
870    pub fn h(&self) -> (i32, &[(u32, u32)]) {
871        (self.top.h, self.top.h_chars.as_slice())
872    }
873    /// Get the current value of the vertical position, _v_.
874    pub fn v(&self) -> i32 {
875        self.top.v
876    }
877    /// Get the current value of a variable.
878    pub fn var(&self, var: Var) -> i32 {
879        self.top.vars[var as usize]
880    }
881    /// Get the current value of the variable _w_.
882    pub fn w(&self) -> i32 {
883        self.var(Var::W)
884    }
885    /// Get the current value of the variable _x_.
886    pub fn x(&self) -> i32 {
887        self.var(Var::X)
888    }
889    /// Get the current value of the variable _y_.
890    pub fn y(&self) -> i32 {
891        self.var(Var::Y)
892    }
893    /// Get the current value of the variable _z_.
894    pub fn z(&self) -> i32 {
895        self.var(Var::Z)
896    }
897}
898
899#[cfg(test)]
900mod tests {
901    use super::*;
902
903    fn run_serialize_test(want: Vec<u8>, op: Op) {
904        let mut got = vec![];
905        op.serialize(&mut got);
906        assert_eq!(got, want);
907    }
908
909    fn run_deserialize_test(b: Vec<u8>, want: Op) {
910        let mut result = Ok(());
911        let got: Vec<Op> = Deserializer::new(&b, &mut result).into_iter().collect();
912        assert_eq!(Ok(()), result);
913        assert_eq!(got, vec![want]);
914    }
915
916    macro_rules! serde_tests {
917        ( $( ($name: ident, [ $($elem: expr),+], $op: expr ), )+  ) => {
918            $(
919            mod $name {
920                use super::*;
921
922                #[test]
923                fn test_serialize() {
924                    let b = vec![ $( $elem, )+ ];
925                    run_serialize_test(b, $op);
926                }
927
928                #[test]
929                fn test_deserialize() {
930                    let b = vec![ $( $elem, )+ ];
931                    run_deserialize_test(b, $op);
932                }
933            }
934            )+
935        };
936    }
937
938    serde_tests!(
939        (
940            op_code_0,
941            [0],
942            Op::TypesetChar {
943                char: 0,
944                move_h: true
945            }
946        ),
947        (
948            op_code_1,
949            [1],
950            Op::TypesetChar {
951                char: 1,
952                move_h: true
953            }
954        ),
955        (
956            op_code_127,
957            [127],
958            Op::TypesetChar {
959                char: 127,
960                move_h: true
961            }
962        ),
963        (
964            op_code_128_case_1,
965            [128, 128],
966            Op::TypesetChar {
967                char: 128,
968                move_h: true
969            }
970        ),
971        (
972            op_code_128_case_2,
973            [128, 129],
974            Op::TypesetChar {
975                char: 129,
976                move_h: true
977            }
978        ),
979        (
980            op_code_128_case_3,
981            [128, 255],
982            Op::TypesetChar {
983                char: 255,
984                move_h: true
985            }
986        ),
987        (
988            op_code_129_case_1,
989            [129, 1, 0],
990            Op::TypesetChar {
991                char: 256,
992                move_h: true
993            }
994        ),
995        (
996            op_code_129_case_2,
997            [129, 1, 2],
998            Op::TypesetChar {
999                char: 256 + 2,
1000                move_h: true
1001            }
1002        ),
1003        (
1004            op_code_129_case_3,
1005            [129, 255, 255],
1006            Op::TypesetChar {
1007                char: 256 * 256 - 1,
1008                move_h: true
1009            }
1010        ),
1011        (
1012            op_code_130_case_1,
1013            [130, 1, 0, 0],
1014            Op::TypesetChar {
1015                char: 256 * 256,
1016                move_h: true
1017            }
1018        ),
1019        (
1020            op_code_130_case_2,
1021            [130, 1, 2, 3],
1022            Op::TypesetChar {
1023                char: 256 * 256 + 2 * 256 + 3,
1024                move_h: true
1025            }
1026        ),
1027        (
1028            op_code_130_case_3,
1029            [130, 255, 255, 255],
1030            Op::TypesetChar {
1031                char: 256 * 256 * 256 - 1,
1032                move_h: true
1033            }
1034        ),
1035        (
1036            op_code_131_case_1,
1037            [131, 1, 0, 0, 0],
1038            Op::TypesetChar {
1039                char: 256 * 256 * 256,
1040                move_h: true
1041            }
1042        ),
1043        (
1044            op_code_131_case_2,
1045            [131, 1, 2, 3, 4],
1046            Op::TypesetChar {
1047                char: 256 * 256 * 256 + 2 * 256 * 256 + 3 * 256 + 4,
1048                move_h: true
1049            }
1050        ),
1051        (
1052            op_code_131_case_3,
1053            [131, 255, 255, 255, 255],
1054            Op::TypesetChar {
1055                char: u32::MAX,
1056                move_h: true
1057            }
1058        ),
1059        (
1060            op_code_132,
1061            [132, 0, 0, 0, 1, 0, 0, 0, 2],
1062            Op::TypesetRule {
1063                height: 1,
1064                width: 2,
1065                move_h: true
1066            }
1067        ),
1068        (
1069            op_code_133,
1070            [133, 1],
1071            Op::TypesetChar {
1072                char: 1,
1073                move_h: false,
1074            }
1075        ),
1076        (
1077            op_code_134,
1078            [134, 255, 255],
1079            Op::TypesetChar {
1080                char: 256 * 256 - 1,
1081                move_h: false,
1082            }
1083        ),
1084        (
1085            op_code_135,
1086            [135, 255, 255, 255],
1087            Op::TypesetChar {
1088                char: 256 * 256 * 256 - 1,
1089                move_h: false,
1090            }
1091        ),
1092        (
1093            op_code_136,
1094            [136, 255, 255, 255, 255],
1095            Op::TypesetChar {
1096                char: u32::MAX,
1097                move_h: false,
1098            }
1099        ),
1100        (
1101            op_code_137,
1102            [137, 0, 0, 0, 1, 0, 0, 0, 2],
1103            Op::TypesetRule {
1104                height: 1,
1105                width: 2,
1106                move_h: false,
1107            }
1108        ),
1109        (op_code_138, [138], Op::NoOp),
1110        (
1111            op_code_139,
1112            [
1113                139, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0,
1114                0, 7, 0, 0, 0, 8, 0, 0, 0, 9, 255, 255, 255, 255, 0, 0, 0, 1
1115            ],
1116            Op::BeginPage {
1117                parameters: [1, 2, 3, 4, 5, 6, 7, 8, 9, -1],
1118                previous_begin_page: 1
1119            }
1120        ),
1121        (op_code_140, [140], Op::EndPage),
1122        (op_code_141, [141], Op::Push),
1123        (op_code_142, [142], Op::Pop),
1124        (op_code_143, [143, 2], Op::Right(2)),
1125        (op_code_144, [144, 1, 2], Op::Right(256 + 2)),
1126        (op_code_145, [145, 1, 0, 2], Op::Right(256 * 256 + 2)),
1127        (
1128            op_code_146,
1129            [146, 1, 0, 0, 2],
1130            Op::Right(256 * 256 * 256 + 2)
1131        ),
1132        (op_code_147, [147], Op::Move(Var::W)),
1133        (op_code_148, [148, 2], Op::SetVar(Var::W, 2)),
1134        (op_code_149, [149, 1, 2], Op::SetVar(Var::W, 256 + 2)),
1135        (
1136            op_code_150,
1137            [150, 1, 0, 2],
1138            Op::SetVar(Var::W, 256 * 256 + 2)
1139        ),
1140        (
1141            op_code_151,
1142            [151, 1, 0, 0, 2],
1143            Op::SetVar(Var::W, 256 * 256 * 256 + 2)
1144        ),
1145        (op_code_152, [152], Op::Move(Var::X)),
1146        (op_code_153, [153, 2], Op::SetVar(Var::X, 2)),
1147        (op_code_154, [154, 1, 2], Op::SetVar(Var::X, 256 + 2)),
1148        (
1149            op_code_155,
1150            [155, 1, 0, 2],
1151            Op::SetVar(Var::X, 256 * 256 + 2)
1152        ),
1153        (
1154            op_code_156,
1155            [156, 1, 0, 0, 2],
1156            Op::SetVar(Var::X, 256 * 256 * 256 + 2)
1157        ),
1158        // We have exhaustive tests for `Op::Down` in order to test all the
1159        // edge cases of i32 variable serding.
1160        (op_code_157_zero, [157, 0], Op::Down(0)),
1161        (op_code_157_least_positive, [157, 1], Op::Down(1)),
1162        (op_code_157_least_negative, [157, 255], Op::Down(-1)),
1163        (op_code_157_most_positive, [157, 127], Op::Down(127)),
1164        (op_code_157_most_negative, [157, 128], Op::Down(-128)),
1165        (
1166            op_code_158_least_positive,
1167            [158, 0, 128],
1168            Op::Down(2_i32.pow(7))
1169        ),
1170        (
1171            op_code_158_least_negative,
1172            [158, 255, 127],
1173            Op::Down(-1 * 2_i32.pow(7) - 1)
1174        ),
1175        (
1176            op_code_158_most_positive,
1177            [158, 127, 255],
1178            Op::Down(2_i32.pow(15) - 1)
1179        ),
1180        (
1181            op_code_158_most_negative,
1182            [158, 128, 0],
1183            Op::Down(-1 * 2_i32.pow(15))
1184        ),
1185        (
1186            op_code_159_least_positive,
1187            [159, 0, 128, 0],
1188            Op::Down(2_i32.pow(15))
1189        ),
1190        (
1191            op_code_159_least_negative,
1192            [159, 255, 127, 255],
1193            Op::Down(-1 * 2_i32.pow(15) - 1)
1194        ),
1195        (
1196            op_code_159_most_positive,
1197            [159, 127, 255, 255],
1198            Op::Down(2_i32.pow(23) - 1)
1199        ),
1200        (
1201            op_code_159_most_negative,
1202            [159, 128, 0, 0],
1203            Op::Down(-1 * 2_i32.pow(23))
1204        ),
1205        (
1206            op_code_160_least_positive,
1207            [160, 0, 128, 0, 0],
1208            Op::Down(2_i32.pow(23))
1209        ),
1210        (
1211            op_code_160_least_negative,
1212            [160, 255, 127, 255, 255],
1213            Op::Down(-1 * 2_i32.pow(23) - 1)
1214        ),
1215        (
1216            op_code_160_most_positive,
1217            [160, 127, 255, 255, 255],
1218            Op::Down(i32::MAX)
1219        ),
1220        (
1221            op_code_160_most_negative,
1222            [160, 128, 0, 0, 0],
1223            Op::Down(i32::MIN)
1224        ),
1225        (op_code_161, [161], Op::Move(Var::Y)),
1226        (op_code_162, [162, 2], Op::SetVar(Var::Y, 2)),
1227        (op_code_163, [163, 1, 2], Op::SetVar(Var::Y, 256 + 2)),
1228        (
1229            op_code_164,
1230            [164, 1, 0, 2],
1231            Op::SetVar(Var::Y, 256 * 256 + 2)
1232        ),
1233        (
1234            op_code_165,
1235            [165, 1, 0, 0, 2],
1236            Op::SetVar(Var::Y, 256 * 256 * 256 + 2)
1237        ),
1238        (op_code_166, [166], Op::Move(Var::Z)),
1239        (op_code_167, [167, 2], Op::SetVar(Var::Z, 2)),
1240        (op_code_168, [168, 1, 2], Op::SetVar(Var::Z, 256 + 2)),
1241        (
1242            op_code_169,
1243            [169, 1, 0, 2],
1244            Op::SetVar(Var::Z, 256 * 256 + 2)
1245        ),
1246        (
1247            op_code_170,
1248            [170, 1, 0, 0, 2],
1249            Op::SetVar(Var::Z, 256 * 256 * 256 + 2)
1250        ),
1251        (op_code_171, [171], Op::EnableFont(0)),
1252        (op_code_234, [234], Op::EnableFont(63)),
1253        (op_code_235, [235, 64], Op::EnableFont(64)),
1254        (op_code_236, [236, 1, 0], Op::EnableFont(256)),
1255        (op_code_237, [237, 1, 0, 0], Op::EnableFont(256 * 256)),
1256        (
1257            op_code_238,
1258            [238, 1, 0, 0, 0],
1259            Op::EnableFont(256 * 256 * 256)
1260        ),
1261        (
1262            op_code_239,
1263            [239, 5, 0, 1, 2, 3, 4],
1264            Op::Extension(vec![0, 1, 2, 3, 4])
1265        ),
1266        (
1267            op_code_243,
1268            [243, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 5, 99, 109, 114, 49, 48],
1269            Op::DefineFont {
1270                number: 1,
1271                checksum: 2,
1272                at_size: 3,
1273                design_size: 4,
1274                area: "".to_string(),
1275                name: "cmr10".to_string(),
1276            }
1277        ),
1278        (
1279            op_code_244,
1280            [244, 1, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 5, 0, 99, 109, 114, 49, 48],
1281            Op::DefineFont {
1282                number: 256 + 1,
1283                checksum: 2,
1284                at_size: 3,
1285                design_size: 4,
1286                area: "cmr10".to_string(),
1287                name: "".to_string(),
1288            }
1289        ),
1290        (
1291            op_code_245,
1292            [245, 1, 1, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 2, 3, 99, 109, 114, 49, 48],
1293            Op::DefineFont {
1294                number: 256 * 256 + 256 + 1,
1295                checksum: 2,
1296                at_size: 3,
1297                design_size: 4,
1298                area: "cm".to_string(),
1299                name: "r10".to_string(),
1300            }
1301        ),
1302        (
1303            op_code_246,
1304            [246, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0],
1305            Op::DefineFont {
1306                number: 256 * 256 * 256,
1307                checksum: 2,
1308                at_size: 3,
1309                design_size: 4,
1310                area: "".to_string(),
1311                name: "".to_string(),
1312            }
1313        ),
1314        (
1315            op_code_247,
1316            [247, 2, 0, 0, 0, 3, 0, 0, 0, 5, 1, 2, 3, 4, 3, 65, 66, 67],
1317            Op::Preamble {
1318                dvi_format: 2,
1319                unit_numerator: 3,
1320                unit_denominator: 5,
1321                magnification: 1 * 256 * 256 * 256 + 2 * 256 * 256 + 3 * 256 + 4,
1322                comment: "ABC".to_string(),
1323            }
1324        ),
1325        (
1326            op_code_248,
1327            [
1328                248, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 7,
1329                0, 8
1330            ],
1331            Op::BeginPostamble {
1332                final_begin_page: 1,
1333                unit_numerator: 2,
1334                unit_denominator: 3,
1335                magnification: 4,
1336                largest_height: 5,
1337                largest_width: 6,
1338                max_stack_depth: 7,
1339                num_pages: 8,
1340            }
1341        ),
1342        (
1343            op_code_249,
1344            [249, 1, 0, 0, 0, 2, 223, 223, 223, 223, 223, 223],
1345            Op::EndPostamble {
1346                dvi_format: 1,
1347                postamble: 2,
1348                num_223_bytes: 6
1349            }
1350        ),
1351    );
1352
1353    #[derive(Default)]
1354    struct WantValues {
1355        f: u32,
1356        h: i32,
1357        h_chars: Vec<(u32, u32)>,
1358        v: i32,
1359        w: i32,
1360        x: i32,
1361        y: i32,
1362        z: i32,
1363    }
1364
1365    fn run_values_test(ops: Vec<Op>, changed: Vec<bool>, want_values: WantValues) {
1366        let mut values: Values = Default::default();
1367        for (i, op) in ops.into_iter().enumerate() {
1368            let want_changed = changed[i];
1369            let got_changed = values.update(&op);
1370            assert_eq!(want_changed, got_changed);
1371        }
1372        assert_eq!(values.f(), want_values.f, "unexpected f value");
1373        assert_eq!(values.h().0, want_values.h, "unexpected h.0 value");
1374        assert_eq!(values.h().1, want_values.h_chars, "unexpected h.1 value");
1375        assert_eq!(values.v(), want_values.v, "unexpected v value");
1376        assert_eq!(values.w(), want_values.w, "unexpected w value");
1377        assert_eq!(values.x(), want_values.x, "unexpected x value");
1378        assert_eq!(values.y(), want_values.y, "unexpected y value");
1379        assert_eq!(values.z(), want_values.z, "unexpected z value");
1380    }
1381
1382    macro_rules! values_tests {
1383        ( $(
1384            (
1385                $name: ident,
1386                [ $( $op: expr ),+ ],
1387                [ $( $changed: expr ),+ ],
1388                $( f: $want_f: expr, )?
1389                $( h: $want_h: expr, )?
1390                $( h_chars: $want_h_chars: expr, )?
1391                $( v: $want_v: expr, )?
1392                $( w: $want_w: expr, )?
1393                $( x: $want_x: expr, )?
1394                $( y: $want_y: expr, )?
1395                $( z: $want_z: expr, )?
1396            ),
1397        )+ ) => {
1398            $(
1399                #[test]
1400                fn $name() {
1401                    let ops = vec![ $( $op, )+ ];
1402                    let changed = vec![ $( $changed, )+ ];
1403                    let mut want_values: WantValues = Default::default();
1404                    $( want_values.f = $want_f; )?
1405                    $( want_values.h = $want_h; )?
1406                    $( want_values.h_chars = $want_h_chars; )?
1407                    $( want_values.v = $want_v; )?
1408                    $( want_values.w = $want_w; )?
1409                    $( want_values.x = $want_x; )?
1410                    $( want_values.y = $want_y; )?
1411                    $( want_values.z = $want_z; )?
1412                    run_values_test(ops, changed, want_values);
1413                }
1414            )+
1415        };
1416    }
1417
1418    values_tests!(
1419        (
1420            noop,
1421            [Op::NoOp],
1422            [false],
1423            f: 0,
1424        ),
1425        (
1426            extension,
1427            [Op::Extension(vec![1,2,3])],
1428            [false],
1429            f: 0,
1430        ),
1431        (
1432            define_font,
1433            [Op::DefineFont{ number: 0, checksum: 1, at_size: 2, design_size: 3, area: "".to_string(), name: "".to_string() }],
1434            [false],
1435            f: 0,
1436        ),
1437        (
1438            preamble,
1439            [Op::Preamble{ dvi_format: 1, unit_numerator: 2, unit_denominator: 3, magnification: 4, comment: "".to_string() }],
1440            [false],
1441            f: 0,
1442        ),
1443        (
1444            begin_postamble,
1445            [Op::BeginPostamble{ final_begin_page: 1, unit_numerator: 2, unit_denominator: 3, magnification: 4, largest_height: 5, largest_width: 6, max_stack_depth: 7, num_pages: 8 }],
1446            [false],
1447            f: 0,
1448        ),
1449        (
1450            end_postamble,
1451            [Op::EndPostamble{ postamble: 1, dvi_format: 2, num_223_bytes: 3 }],
1452            [false],
1453            f: 0,
1454        ),
1455        (
1456            enable_font_1,
1457            [Op::EnableFont(3), Op::EnableFont(3)],
1458            [true, false],
1459            f: 3,
1460        ),
1461        (
1462            enable_font_2,
1463            [Op::EnableFont(3), Op::Push, Op::EnableFont(5), Op::Pop],
1464            [true, false, true, false],
1465            f: 5,
1466        ),
1467        (
1468            var_w_1,
1469            [Op::SetVar(Var::W, 5), Op::SetVar(Var::W, 5)],
1470            [true, true],
1471            h: 10,
1472            w: 5,
1473        ),
1474        (
1475            var_w_2,
1476            [Op::SetVar(Var::W, 5), Op::Push, Op::SetVar(Var::W, 3), Op::Pop],
1477            [true, false, true, true],
1478            h: 5,
1479            w: 5,
1480        ),
1481        (
1482            var_w_3,
1483            [Op::SetVar(Var::W, 5), Op::Push, Op::SetVar(Var::W, 5), Op::Pop],
1484            [true, false, true, true],
1485            h: 5,
1486            w: 5,
1487        ),
1488        (
1489            var_w_4,
1490            [Op::SetVar(Var::W, 5), Op::SetVar(Var::W, 0), Op::SetVar(Var::W, 0)],
1491            [true, true, false],
1492            h: 5,
1493            w: 0,
1494        ),
1495        (
1496            var_w_5,
1497            [Op::SetVar(Var::W, 5), Op::Move(Var::W)],
1498            [true, true],
1499            h: 10,
1500            w: 5,
1501        ),
1502        (
1503            var_w_6,
1504            [Op::SetVar(Var::W, 0), Op::Move(Var::W)],
1505            [false, false],
1506            h: 0,
1507            w: 0,
1508        ),
1509        (
1510            var_x,
1511            [Op::SetVar(Var::X, 5), Op::SetVar(Var::X, 5), Op::Move(Var::X)],
1512            [true, true, true],
1513            h: 15,
1514            x: 5,
1515        ),
1516        (
1517            var_y,
1518            [Op::SetVar(Var::Y, 5), Op::SetVar(Var::Y, 5), Op::Move(Var::Y)],
1519            [true, true, true],
1520            v: 15,
1521            y: 5,
1522        ),
1523        (
1524            var_z,
1525            [Op::SetVar(Var::Z, 5), Op::SetVar(Var::Z, 5), Op::Move(Var::Z)],
1526            [true, true, true],
1527            v: 15,
1528            z: 5,
1529        ),
1530        (
1531            right_1,
1532            [Op::Right(5)],
1533            [true],
1534            h: 5,
1535        ),
1536        (
1537            right_2,
1538            [Op::Right(0)],
1539            [false],
1540            h: 0,
1541        ),
1542        (
1543            down_1,
1544            [Op::Down(5)],
1545            [true],
1546            v: 5,
1547        ),
1548        (
1549            down_2,
1550            [Op::Down(0)],
1551            [false],
1552            v: 0,
1553        ),
1554        (
1555            rule_1,
1556            [Op::TypesetRule{height: 2, width: 3, move_h: true}],
1557            [true],
1558            h: 3,
1559        ),
1560        (
1561            rule_2,
1562            [Op::TypesetRule{height: 2, width: 0, move_h: true}],
1563            [false],
1564            h: 0,
1565        ),
1566        (
1567            rule_3,
1568            [Op::TypesetRule{height: 2, width: 3, move_h: false}],
1569            [false],
1570            h: 0,
1571        ),
1572        (
1573            begin_page_1,
1574            [
1575                Op::SetVar(Var::W, 1),
1576                Op::SetVar(Var::X, 2),
1577                Op::SetVar(Var::Y, 3),
1578                Op::SetVar(Var::Z, 4),
1579                Op::BeginPage { parameters: Default::default(), previous_begin_page: 1 }
1580            ],
1581            [true, true, true, true, true],
1582            h: 0,
1583            v: 0,
1584            w: 0,
1585            x: 0,
1586            y: 0,
1587            z: 0,
1588        ),
1589        (
1590            begin_page_2,
1591            [
1592                Op::SetVar(Var::W, 0),
1593                Op::SetVar(Var::X, 0),
1594                Op::SetVar(Var::Y, 0),
1595                Op::SetVar(Var::Z, 0),
1596                Op::BeginPage { parameters: Default::default(), previous_begin_page: 1 }
1597            ],
1598            [false, false, false, false, false],
1599            h: 0,
1600            v: 0,
1601            w: 0,
1602            x: 0,
1603            y: 0,
1604            z: 0,
1605        ),
1606        (
1607            end_page,
1608            [Op::EndPage],
1609            [false],
1610            h: 0,
1611        ),
1612        (
1613            typeset_char_1,
1614            [Op::TypesetChar{char: 1, move_h: true}],
1615            [true],
1616            h: 0,
1617            h_chars: vec![(1, 0)],
1618        ),
1619        (
1620            typeset_char_2,
1621            [Op::TypesetChar{char: 1, move_h: false}],
1622            [false],
1623            h: 0,
1624            h_chars: vec![],
1625        ),
1626        (
1627            typeset_char_3,
1628            [Op::Push, Op::TypesetChar{char: 1, move_h: true}, Op::Pop],
1629            [false, true, true],
1630            h: 0,
1631            h_chars: vec![],
1632        ),
1633    );
1634}