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
//! The tftopl and pltotf algorithms.

use std::fmt::Write;

/// Output of the tftopl algorithm.
pub struct TfmToPlOutput {
    pub pl_data: Result<String, crate::format::DeserializationError>,
    pub error_messages: Vec<TfmToPlErrorMessage>,
}

/// An error message written by the tftopl algorithm.
pub enum TfmToPlErrorMessage {
    DeserializationWarning(crate::format::DeserializationWarning),
    ValidationWarning(crate::format::ValidationWarning),
}

impl TfmToPlErrorMessage {
    pub fn tftopl_message(&self) -> String {
        match self {
            TfmToPlErrorMessage::DeserializationWarning(warning) => warning.tftopl_message(),
            TfmToPlErrorMessage::ValidationWarning(warning) => warning.tftopl_message(),
        }
    }
}

/// The tftopl algorithm.
///
/// This algorithm converts .tfm bytes to a .pl string.
pub fn tfm_to_pl(
    tfm_data: &[u8],
    indent: usize,
    display_format: &dyn Fn(&crate::pl::File) -> crate::pl::CharDisplayFormat,
) -> Result<TfmToPlOutput, std::fmt::Error> {
    let mut error_messages = Vec::<TfmToPlErrorMessage>::new();
    let (tfm_file_or, warnings) = crate::format::File::deserialize(tfm_data);
    for warning in warnings {
        error_messages.push(TfmToPlErrorMessage::DeserializationWarning(warning));
    }
    let mut tfm_file = match tfm_file_or {
        Ok(tfm_file) => tfm_file,
        Err(err) => {
            return Ok(TfmToPlOutput {
                pl_data: Err(err),
                error_messages,
            });
        }
    };
    let warnings = tfm_file.validate_and_fix();
    let infinite_loop = warnings.iter().any(|w| {
        matches!(
            w,
            crate::format::ValidationWarning::LigKernWarning(
                crate::ligkern::lang::ValidationWarning::InfiniteLoop(_),
            )
        )
    });
    let tfm_modified = warnings
        .iter()
        .map(crate::format::ValidationWarning::tfm_file_modified)
        .any(|t| t);
    for warning in warnings {
        error_messages.push(TfmToPlErrorMessage::ValidationWarning(warning));
    }
    let pl_file: crate::pl::File = tfm_file.into();
    let suffix = if infinite_loop {
        "(INFINITE LIGATURE LOOP MUST BE BROKEN!)"
    } else if tfm_modified {
        "(COMMENT THE TFM FILE WAS BAD, SO THE DATA HAS BEEN CHANGED!)\n"
    } else {
        ""
    };
    let mut s = String::new();
    write![
        &mut s,
        "{}{}",
        pl_file.display(indent, display_format(&pl_file),),
        suffix
    ]?;
    Ok(TfmToPlOutput {
        pl_data: Ok(s),
        error_messages,
    })
}