1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
//! Transforms on DVI data.
//!
//! The DVI format can represent the same document
//! in multiple different ways.
//! For example this list of operations:
//! ```
//! vec![
//! dvi::Op::SetVar(dvi::Var::X, 3),
//! dvi::Op::Move(dvi::Var::X),
//! dvi::Op::TypesetChar{char: 'D' as u32, move_h: false},
//! ];
//! ```
//! describes the same document as this list of operations:
//! ```
//! vec![
//! dvi::Op::Right(6),
//! dvi::Op::TypesetChar{char: 'D' as u32, move_h: false},
//! ];
//! ```
//! In both cases, the result of the DVI operations is a document
//! with the single character D typeset at the coordinate (6,0).
//!
//! This module contains _transforms_ that change the DVI representation
//! of documents without changing the actual meaning of the document.
//! You can think of transforms as being like optimization passes in
//! an optimizing compiler: the output program behaves the same,
//! but its code is different.
//!
//! There are at least two reasons why one would want to perform a DVI transform:
//!
//! 1. To **optimize** the DVI in some way; for example, to reduce
//! the size of the DVI file.
//! Knuth performs an optmization of this type in TeX.2021.604-615.
//!
//! 2. To **normalize** the DVI file so that a DVI produced by one program
//! will match the DVI produced by another program.
use super::*;
/// Transform that removes uses of the [`Var`] variables.
///
/// This transform removes all [`Op::Move`] and [`Op::SetVar`]
/// operations and thus removes all uses of the _w_, _x_, _y_ and
/// _z_ variables.
/// All [`Op::Move`] and [`Op::SetVar`] are replaced by [`Op::Right`]
/// and [`Op::Down`] operations, with the correct payload.
///
/// This transform undoes the optimization that Knuth performs in
/// TeX.2021.604-615.
///
/// ```
/// use dvi::transforms::VarRemover;
/// let ops_1 = vec![
/// dvi::Op::SetVar(dvi::Var::X, 3),
/// dvi::Op::Push,
/// dvi::Op::SetVar(dvi::Var::X, 5),
/// dvi::Op::Move(dvi::Var::X),
/// dvi::Op::Pop,
/// dvi::Op::Move(dvi::Var::X),
/// ];
/// let ops_2: Vec<dvi::Op> = VarRemover::new(ops_1).collect();
/// assert_eq![
/// ops_2,
/// vec![
/// dvi::Op::Right(3),
/// dvi::Op::Push,
/// dvi::Op::Right(5),
/// dvi::Op::Right(5),
/// dvi::Op::Pop,
/// dvi::Op::Right(3),
/// ],
/// ];
/// ```
pub struct VarRemover<I> {
iter: I,
values: Values,
}
impl<I: Iterator<Item = Op>> VarRemover<I> {
pub fn new<J: IntoIterator<IntoIter = I>>(iter: J) -> Self {
Self {
iter: iter.into_iter(),
values: Default::default(),
}
}
}
impl<I: Iterator<Item = Op>> Iterator for VarRemover<I> {
type Item = Op;
fn next(&mut self) -> Option<Self::Item> {
let op = self.iter.next()?;
self.values.update(&op);
Some(match op {
Op::Move(var) | Op::SetVar(var, _) => {
let value = self.values.var(var);
match var {
Var::W | Var::X => Op::Right(value),
Var::Y | Var::Z => Op::Down(value),
}
}
_ => op,
})
}
}