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}