2025-10-19 00:05:16 +01:00
|
|
|
//! From <https://en.wikipedia.org/wiki/ANSI_escape_code#SGR>_(`Select_Graphic_Rendition`)_parameters
|
2025-06-27 02:31:23 +01:00
|
|
|
|
|
|
|
|
use std::slice::Iter;
|
|
|
|
|
|
|
|
|
|
use crate::warn;
|
|
|
|
|
|
|
|
|
|
use crate::ansi_to_image::color::{Color, ColorType};
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub(super) enum EscapeSequence {
|
|
|
|
|
Reset,
|
|
|
|
|
|
|
|
|
|
BlackLetterFont,
|
|
|
|
|
Bold,
|
|
|
|
|
Faint,
|
|
|
|
|
Italic,
|
|
|
|
|
RapidBlink,
|
|
|
|
|
SlowBlink,
|
|
|
|
|
Underline,
|
|
|
|
|
|
|
|
|
|
NotBold,
|
|
|
|
|
NotUnderline,
|
|
|
|
|
NormalIntensity,
|
|
|
|
|
NotItalicNorBlackLetter,
|
|
|
|
|
NotBlinking,
|
|
|
|
|
|
|
|
|
|
ReverseVideo,
|
|
|
|
|
Conceal,
|
|
|
|
|
CrossedOut,
|
|
|
|
|
|
|
|
|
|
DefaultForegroundColor,
|
|
|
|
|
DefaultBackgroundColor,
|
|
|
|
|
|
|
|
|
|
PrimaryFont,
|
|
|
|
|
SetAlternativeFont,
|
|
|
|
|
|
|
|
|
|
ForegroundColor(ColorType),
|
|
|
|
|
BackgroundColor(ColorType),
|
|
|
|
|
|
|
|
|
|
DisableProportionalSpacing,
|
|
|
|
|
NeitherSuperscriptNorSubscript,
|
|
|
|
|
|
|
|
|
|
NotReserved,
|
|
|
|
|
|
|
|
|
|
Unimplemented(Vec<u16>),
|
|
|
|
|
Ignore,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl EscapeSequence {
|
2025-10-19 00:05:16 +01:00
|
|
|
pub(super) fn parse_params(params: &[&u16]) -> Vec<EscapeSequence> {
|
2025-06-27 02:31:23 +01:00
|
|
|
let iter = &mut params.iter();
|
|
|
|
|
let mut result = vec![];
|
|
|
|
|
while iter.len() > 0 {
|
2025-10-19 00:05:16 +01:00
|
|
|
result.push(Self::consume_and_parse(iter));
|
2025-06-27 02:31:23 +01:00
|
|
|
}
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
fn consume_and_parse(iter: &mut Iter<&u16>) -> Self {
|
|
|
|
|
if let Some(current) = iter.next() {
|
|
|
|
|
return match *current {
|
|
|
|
|
0 => Self::Reset,
|
|
|
|
|
1 => Self::Bold,
|
|
|
|
|
2 => Self::Faint,
|
|
|
|
|
3 => Self::Italic,
|
|
|
|
|
4 => Self::Underline,
|
|
|
|
|
5 => Self::SlowBlink,
|
|
|
|
|
6 => Self::RapidBlink,
|
|
|
|
|
|
|
|
|
|
7 => Self::ReverseVideo,
|
|
|
|
|
8 => Self::Conceal,
|
2025-10-19 00:05:16 +01:00
|
|
|
9 | 53 => Self::CrossedOut,
|
2025-06-27 02:31:23 +01:00
|
|
|
|
|
|
|
|
10 => Self::PrimaryFont,
|
|
|
|
|
|
2025-10-19 00:05:16 +01:00
|
|
|
11..=19 => Self::SetAlternativeFont,
|
2025-06-27 02:31:23 +01:00
|
|
|
|
|
|
|
|
20 => Self::BlackLetterFont,
|
|
|
|
|
21 => Self::NotBold,
|
|
|
|
|
22 => Self::NormalIntensity,
|
|
|
|
|
23 => Self::NotItalicNorBlackLetter,
|
|
|
|
|
24 => Self::NotUnderline,
|
|
|
|
|
25 => Self::NotBlinking,
|
|
|
|
|
|
2025-10-19 00:05:16 +01:00
|
|
|
26 | 28 | 29 => Self::Ignore, // Proportional spacing, Reveal, Not crossed out
|
2025-06-27 02:31:23 +01:00
|
|
|
|
|
|
|
|
27 => Self::NotReserved,
|
|
|
|
|
|
|
|
|
|
30 => Self::ForegroundColor(ColorType::Normal(Color::Black)),
|
|
|
|
|
31 => Self::ForegroundColor(ColorType::Normal(Color::Red)),
|
|
|
|
|
32 => Self::ForegroundColor(ColorType::Normal(Color::Green)),
|
|
|
|
|
33 => Self::ForegroundColor(ColorType::Normal(Color::Yellow)),
|
|
|
|
|
34 => Self::ForegroundColor(ColorType::Normal(Color::Blue)),
|
|
|
|
|
35 => Self::ForegroundColor(ColorType::Normal(Color::Magenta)),
|
|
|
|
|
36 => Self::ForegroundColor(ColorType::Normal(Color::Cyan)),
|
|
|
|
|
37 => Self::ForegroundColor(ColorType::Normal(Color::White)),
|
2025-10-19 00:05:16 +01:00
|
|
|
38 => {
|
|
|
|
|
if let Some(mode) = iter.next() {
|
|
|
|
|
Self::ForegroundColor(parse_color(**mode, iter))
|
|
|
|
|
} else {
|
2025-06-27 02:31:23 +01:00
|
|
|
warn!(
|
|
|
|
|
"[SEQUENCE_PARSER] foreground color mode is not supplied, parse_color(null, ...)",
|
|
|
|
|
);
|
|
|
|
|
Self::Ignore
|
|
|
|
|
}
|
2025-10-19 00:05:16 +01:00
|
|
|
}
|
2025-06-27 02:31:23 +01:00
|
|
|
39 => Self::DefaultForegroundColor,
|
|
|
|
|
|
|
|
|
|
40 => Self::BackgroundColor(ColorType::Normal(Color::Black)),
|
|
|
|
|
41 => Self::BackgroundColor(ColorType::Normal(Color::Red)),
|
|
|
|
|
42 => Self::BackgroundColor(ColorType::Normal(Color::Green)),
|
|
|
|
|
43 => Self::BackgroundColor(ColorType::Normal(Color::Yellow)),
|
|
|
|
|
44 => Self::BackgroundColor(ColorType::Normal(Color::Blue)),
|
|
|
|
|
45 => Self::BackgroundColor(ColorType::Normal(Color::Magenta)),
|
|
|
|
|
46 => Self::BackgroundColor(ColorType::Normal(Color::Cyan)),
|
|
|
|
|
47 => Self::BackgroundColor(ColorType::Normal(Color::White)),
|
2025-10-19 00:05:16 +01:00
|
|
|
48 => {
|
|
|
|
|
if let Some(mode) = iter.next() {
|
|
|
|
|
Self::BackgroundColor(parse_color(**mode, iter))
|
|
|
|
|
} else {
|
2025-06-27 02:31:23 +01:00
|
|
|
warn!(
|
|
|
|
|
"[SEQUENCE_PARSER] background color mode is not supplied, parse_color(null, ...)",
|
|
|
|
|
);
|
|
|
|
|
Self::Ignore
|
|
|
|
|
}
|
2025-10-19 00:05:16 +01:00
|
|
|
}
|
2025-06-27 02:31:23 +01:00
|
|
|
49 => Self::DefaultBackgroundColor,
|
|
|
|
|
50 => Self::DisableProportionalSpacing,
|
|
|
|
|
|
|
|
|
|
75 => Self::NeitherSuperscriptNorSubscript,
|
|
|
|
|
|
|
|
|
|
90 => Self::ForegroundColor(ColorType::Bright(Color::Black)),
|
|
|
|
|
91 => Self::ForegroundColor(ColorType::Bright(Color::Red)),
|
|
|
|
|
92 => Self::ForegroundColor(ColorType::Bright(Color::Green)),
|
|
|
|
|
93 => Self::ForegroundColor(ColorType::Bright(Color::Yellow)),
|
|
|
|
|
94 => Self::ForegroundColor(ColorType::Bright(Color::Blue)),
|
|
|
|
|
95 => Self::ForegroundColor(ColorType::Bright(Color::Magenta)),
|
|
|
|
|
96 => Self::ForegroundColor(ColorType::Bright(Color::Cyan)),
|
|
|
|
|
97 => Self::ForegroundColor(ColorType::Bright(Color::White)),
|
|
|
|
|
|
|
|
|
|
100 => Self::BackgroundColor(ColorType::Bright(Color::Black)),
|
|
|
|
|
101 => Self::BackgroundColor(ColorType::Bright(Color::Red)),
|
|
|
|
|
102 => Self::BackgroundColor(ColorType::Bright(Color::Green)),
|
|
|
|
|
103 => Self::BackgroundColor(ColorType::Bright(Color::Yellow)),
|
|
|
|
|
104 => Self::BackgroundColor(ColorType::Bright(Color::Blue)),
|
|
|
|
|
105 => Self::BackgroundColor(ColorType::Bright(Color::Magenta)),
|
|
|
|
|
106 => Self::BackgroundColor(ColorType::Bright(Color::Cyan)),
|
|
|
|
|
107 => Self::BackgroundColor(ColorType::Bright(Color::White)),
|
|
|
|
|
|
|
|
|
|
v => Self::Unimplemented(vec![3, *v]),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
Self::Ignore
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-19 00:05:16 +01:00
|
|
|
fn parse_color(mode: u16, iter: &mut Iter<&u16>) -> ColorType {
|
2025-06-27 02:31:23 +01:00
|
|
|
match mode {
|
|
|
|
|
5 => {
|
|
|
|
|
let color = iter.next();
|
|
|
|
|
if let Some(color) = color {
|
2025-10-19 00:05:16 +01:00
|
|
|
#[allow(clippy::cast_possible_truncation)]
|
|
|
|
|
// ANSI color values are known to be 0-255
|
2025-09-20 15:18:58 +01:00
|
|
|
match color {
|
2025-06-27 02:31:23 +01:00
|
|
|
0 => ColorType::Normal(Color::Black),
|
|
|
|
|
1 => ColorType::Normal(Color::Red),
|
|
|
|
|
2 => ColorType::Normal(Color::Green),
|
|
|
|
|
3 => ColorType::Normal(Color::Yellow),
|
|
|
|
|
4 => ColorType::Normal(Color::Blue),
|
|
|
|
|
5 => ColorType::Normal(Color::Magenta),
|
|
|
|
|
6 => ColorType::Normal(Color::Cyan),
|
|
|
|
|
7 => ColorType::Normal(Color::White),
|
|
|
|
|
|
|
|
|
|
8 => ColorType::Bright(Color::Black),
|
|
|
|
|
9 => ColorType::Bright(Color::Red),
|
|
|
|
|
10 => ColorType::Bright(Color::Green),
|
|
|
|
|
11 => ColorType::Bright(Color::Yellow),
|
|
|
|
|
12 => ColorType::Bright(Color::Blue),
|
|
|
|
|
13 => ColorType::Bright(Color::Magenta),
|
|
|
|
|
14 => ColorType::Bright(Color::Cyan),
|
|
|
|
|
15 => ColorType::Bright(Color::White),
|
|
|
|
|
|
|
|
|
|
// These are fixed colors and could be used like ansi 38;5;numberm or 48;5;numberm
|
|
|
|
|
16..=255 => ColorType::Fixed(**color as u8),
|
|
|
|
|
|
|
|
|
|
v => {
|
2025-10-19 00:05:16 +01:00
|
|
|
warn!(
|
|
|
|
|
"[COLOR_PARSER] fixed color value out of range, parse_fixed_color(code: {})",
|
|
|
|
|
v
|
|
|
|
|
);
|
2025-09-20 15:18:58 +01:00
|
|
|
ColorType::PrimaryForeground
|
2025-06-27 02:31:23 +01:00
|
|
|
}
|
2025-09-20 15:18:58 +01:00
|
|
|
}
|
2025-06-27 02:31:23 +01:00
|
|
|
} else {
|
|
|
|
|
warn!(
|
|
|
|
|
"[COLOR_PARSER] fixed color value not supplied, parse_fixed_color(code: null)"
|
|
|
|
|
);
|
|
|
|
|
|
2025-09-20 15:18:58 +01:00
|
|
|
ColorType::PrimaryForeground
|
2025-06-27 02:31:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-19 00:05:16 +01:00
|
|
|
2 => {
|
|
|
|
|
#[allow(clippy::cast_possible_truncation)] // RGB values are known to be 0-255
|
|
|
|
|
match (iter.next(), iter.next(), iter.next()) {
|
|
|
|
|
(Some(r), Some(g), Some(b)) => ColorType::Rgb {
|
2025-06-27 02:31:23 +01:00
|
|
|
field1: (**r as u8, **g as u8, **b as u8),
|
2025-10-19 00:05:16 +01:00
|
|
|
},
|
|
|
|
|
(r, g, b) => {
|
|
|
|
|
warn!(
|
|
|
|
|
"[COLOR_PARSER] rgb color value not supplied (correctly), parse_rgb_color({}, {}, {})",
|
|
|
|
|
r.map_or("null".to_string(), std::string::ToString::to_string),
|
|
|
|
|
g.map_or("null".to_string(), std::string::ToString::to_string),
|
|
|
|
|
b.map_or("null".to_string(), std::string::ToString::to_string)
|
|
|
|
|
);
|
|
|
|
|
ColorType::PrimaryForeground
|
2025-09-20 15:18:58 +01:00
|
|
|
}
|
2025-06-27 02:31:23 +01:00
|
|
|
}
|
2025-10-19 00:05:16 +01:00
|
|
|
}
|
2025-06-27 02:31:23 +01:00
|
|
|
v => {
|
|
|
|
|
warn!(
|
|
|
|
|
"[COLOR_PARSER] color mode is not supplied correctly, parse_color({}, ...)",
|
|
|
|
|
v
|
|
|
|
|
);
|
2025-09-20 15:18:58 +01:00
|
|
|
ColorType::PrimaryForeground
|
2025-06-27 02:31:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|