tfm/algorithms/
mod.rs

1//! The tftopl and pltotf algorithms.
2
3use crate::ValidationWarning;
4use std::fmt::Write;
5
6/// Output of the tftopl algorithm.
7pub struct TfmToPlOutput {
8    pub pl_data: Result<String, crate::DeserializationError>,
9    pub error_messages: Vec<TfmToPlErrorMessage>,
10}
11
12/// An error message written by the tftopl algorithm.
13pub enum TfmToPlErrorMessage {
14    DeserializationWarning(crate::DeserializationWarning),
15    ValidationWarning(crate::ValidationWarning),
16}
17
18impl TfmToPlErrorMessage {
19    pub fn tftopl_message(&self) -> String {
20        match self {
21            TfmToPlErrorMessage::DeserializationWarning(warning) => warning.tftopl_message(),
22            TfmToPlErrorMessage::ValidationWarning(warning) => warning.tftopl_message(),
23        }
24    }
25}
26/// The pltotf algorithm.
27///
28/// This algorithm converts a .pl string to .tfm bytes.
29pub fn pl_to_tfm(pl_data: &str) -> (Vec<u8>, Vec<crate::pl::ParseWarning>) {
30    let (pl_file, warnings) = crate::pl::File::from_pl_source_code(pl_data);
31    let tfm_file: crate::File = pl_file.into();
32    let tfm_output: Vec<u8> = tfm_file.serialize();
33    (tfm_output, warnings)
34}
35
36/// The tftopl algorithm.
37///
38/// This algorithm converts .tfm bytes to a .pl string.
39pub fn tfm_to_pl(
40    tfm_data: &[u8],
41    indent: usize,
42    display_format: &dyn Fn(&crate::pl::File) -> crate::pl::CharDisplayFormat,
43) -> Result<TfmToPlOutput, std::fmt::Error> {
44    let mut error_messages = Vec::<TfmToPlErrorMessage>::new();
45    let (tfm_file_or, warnings) = crate::File::deserialize(tfm_data);
46    for warning in warnings {
47        error_messages.push(TfmToPlErrorMessage::DeserializationWarning(warning));
48    }
49    let mut tfm_file = match tfm_file_or {
50        Ok(tfm_file) => tfm_file,
51        Err(err) => {
52            return Ok(TfmToPlOutput {
53                pl_data: Err(err),
54                error_messages,
55            });
56        }
57    };
58    let warnings = tfm_file.validate_and_fix();
59    let infinite_loop = warnings.iter().any(|w| {
60        matches!(
61            w,
62            crate::ValidationWarning::LigKernWarning(
63                crate::ligkern::lang::ValidationWarning::InfiniteLoop(_),
64            )
65        )
66    });
67    let warnings = filter_lig_kern_warnings(warnings);
68    let tfm_modified = warnings
69        .iter()
70        .any(crate::ValidationWarning::tfm_file_modified);
71    for warning in warnings {
72        error_messages.push(TfmToPlErrorMessage::ValidationWarning(warning));
73    }
74    let pl_file: crate::pl::File = tfm_file.into();
75    let suffix = if infinite_loop {
76        "(INFINITE LIGATURE LOOP MUST BE BROKEN!)"
77    } else if tfm_modified {
78        "(COMMENT THE TFM FILE WAS BAD, SO THE DATA HAS BEEN CHANGED!)\n"
79    } else {
80        ""
81    };
82    let mut s = String::new();
83    write![
84        &mut s,
85        "{}{}",
86        pl_file.display(indent, display_format(&pl_file),),
87        suffix
88    ]?;
89    Ok(TfmToPlOutput {
90        pl_data: Ok(s),
91        error_messages,
92    })
93}
94
95fn filter_lig_kern_warnings(mut warnings: Vec<ValidationWarning>) -> Vec<ValidationWarning> {
96    let mut out = Vec::with_capacity(warnings.len());
97    let mut seen_lig_kern = false;
98    while let Some(warning) = warnings.pop() {
99        if matches!(
100            warning,
101            crate::ValidationWarning::LigKernWarning(
102                crate::ligkern::lang::ValidationWarning::InfiniteLoop(_),
103            )
104        ) {
105            if seen_lig_kern {
106                continue;
107            }
108            seen_lig_kern = true;
109        }
110        out.push(warning);
111    }
112    out.reverse();
113    out
114}