Struct tfm::NextLargerProgram

source ·
pub struct NextLargerProgram { /* private fields */ }
Expand description

Compiled program of “next larger character” instructions

The .tfm file format can associate a “next larger” character to any character in a font. Next larger characters form sequences: i.e. B can be the next larger character for A, and C can be the next larger character for B, leading to the sequences A-B-C. These next larger characters are used at certain points in TeX. TeX occasionally traverses the entire sequence for a given starting character (e.g. A).

As with ligatures, next larger specifications can contain infinite loops - e.g, if X is the next larger character for Y and Y is the next larger character for X. These loops are invalid and removed by TFtoPL and PLtoTF.

Motivated by the idea of “parse don’t validate”, this type represents a compiled version of the next larger specifications in which infinite loops are statically guaranteed not to exist.

The basic use of a valid program looks like this:

let edges = vec![
    (Char::A, Char::B),
    (Char::B, Char::C),
];
let (next_larger_program, warnings) = NextLargerProgram::new(edges.into_iter(), |_| true, true);

assert_eq![warnings, vec![]];

let sequence_A: Vec<Char> = next_larger_program.get(Char::A).collect();
assert_eq!(sequence_A, vec![Char::B, Char::C]);

let sequence_B: Vec<Char> = next_larger_program.get(Char::B).collect();
assert_eq!(sequence_B, vec![Char::C]);

let sequence_C: Vec<Char> = next_larger_program.get(Char::C).collect();
assert_eq!(sequence_C, vec![]);

// Character that is not in the program.
let sequence_D: Vec<Char> = next_larger_program.get(Char::D).collect();
assert_eq!(sequence_D, vec![]);

Warnings

There are two types of error that can occur when constructing the next larger program. Both of these errors are handled gracefully, so we officially refer to them as warnings. The constructor returns them as values of type NextLargerProgramWarning.

Infinite loops

The first error is that next larger programs can contain infinite loops - e.g, if X is the next larger character for Y and Y is the next larger character for X. In this case the loop is broken by removing the next larger program for the character with the largest 8-bit code, in this case Y. A NextLargerProgramWarning::InfiniteLoop warning is returned from the program constructor.

let edges = vec![
    (Char::X, Char::Y),
    (Char::Y, Char::X),
];
let (next_larger_program, warnings) = NextLargerProgram::new(edges.into_iter(), |_| true, true);

assert_eq!(warnings, vec![NextLargerProgramWarning::InfiniteLoop{
    original: Char::Y,
    next_larger: Char::X,
}]);

let sequence_X: Vec<Char> = next_larger_program.get(Char::X).collect();
assert_eq!(sequence_X, vec![Char::Y]);

let sequence_Y: Vec<Char> = next_larger_program.get(Char::Y).collect();
assert_eq!(sequence_Y, vec![]);

Non-existent characters

The second error is that characters referred to in the next larger program may not be defined in the .tfm or .pl file. For example, a .pl file may contain the snippet (CHARACTER C X (NEXTLARGER C Y)) without defining the character Y. The constructor NextLargerProgram::new accepts a function for checking if a character exists. In all cases a NextLargerProgramWarning::NonExistentCharacter warning is returned if a non-existent character is encountered.

The behavior of the resulting next larger program is configured using the drop_non_existent_characters argument. If this is false, then the behavior is the same as PLtoTF and the program still contains the character.

let edges = vec![
    (Char::X, Char::Y),
];
let character_exists = |c| {
    if c == Char::Y {
        false
    } else {
        true
    }
};
let (next_larger_program, warnings) = NextLargerProgram::new(edges.into_iter(), character_exists, false);

assert_eq!(warnings, vec![NextLargerProgramWarning::NonExistentCharacter{
    original: Char::X,
    next_larger: Char::Y,
}]);

let sequence_X: Vec<Char> = next_larger_program.get(Char::X).collect();
assert_eq!(sequence_X, vec![Char::Y]);

If drop_non_existent_characters is true, next larger instructions pointing at non-existent characters are dropped. This is how TFtoPL behaves.

let edges = vec![
    (Char::X, Char::Y),
];
let character_exists = |c| {
    if c == Char::Y {
        false
    } else {
        true
    }
};
let (next_larger_program, warnings) = NextLargerProgram::new(edges.into_iter(), character_exists, true);

assert_eq!(warnings, vec![NextLargerProgramWarning::NonExistentCharacter{
    original: Char::X,
    next_larger: Char::Y,
}]);

let sequence_X: Vec<Char> = next_larger_program.get(Char::X).collect();
assert_eq!(sequence_X, vec![]);

Implementations§

source§

impl NextLargerProgram

source

pub fn new<I: Iterator<Item = (Char, Char)>, F: Fn(Char) -> bool>( edges: I, character_exists: F, drop_non_existent_characters: bool ) -> (Self, Vec<NextLargerProgramWarning>)

Build a new next larger program from an iterator over edges.

source

pub fn get(&self, c: Char) -> impl Iterator<Item = Char> + '_

Get the next larger sequence for a character

source

pub fn is_seven_bit_safe(&self) -> bool

Returns whether this program is seven-bit safe.

A next larger program is seven-bit safe if the next larger sequences for seven-bit characters only contain seven-bit characters. Conversely a program is seven-bit unsafe if there is a seven-bit character whose next larger sequence contains a non-seven-bit character.

let edges = vec![
    (Char(250), Char(125)),
    (Char(125), Char(126)),
];
let (next_larger_program, _) = NextLargerProgram::new(edges.into_iter(), |_| true, true);
assert_eq!(true, next_larger_program.is_seven_bit_safe());

let edges = vec![
    (Char(125), Char(250)),
];
let (next_larger_program, _) = NextLargerProgram::new(edges.into_iter(), |_| true, true);
assert_eq!(false, next_larger_program.is_seven_bit_safe());

Trait Implementations§

source§

impl Clone for NextLargerProgram

source§

fn clone(&self) -> NextLargerProgram

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for NextLargerProgram

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.