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