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}