Add new braille character table and color schemes (#247)

* Add new braille character table and color schemes

* Add character for tab and change color for 0x7f

---------

Co-authored-by: David Peter <mail@david-peter.de>
This commit is contained in:
Niclas SchwarzloseandDavid Peter authored and GitHub committed 2025-08-31 21:13:57 +02:00
1 parent 9ef7c346dd
commit f2547aed69
4 files changed
+179 -14

No files matched your search

+93 -13
View File
@@ -43,6 +43,24 @@ pub enum CharacterTable {
/// Uses code page 437 (for non-ASCII bytes).
#[value(name = "codepage-437")]
CP437,
/// Uses braille characters for non-printable bytes.
Braille,
}
#[derive(Copy, Clone, Debug, Default, ValueEnum)]
#[non_exhaustive]
pub enum ColorScheme {
/// Show the default colors: bright black for NULL bytes, green for ASCII
/// space characters and non-printable ASCII, cyan for printable ASCII characters,
/// and yellow for non-ASCII bytes.
#[default]
Default,
/// Show bright black for NULL bytes, cyan for printable ASCII characters, a gradient
/// from pink to violet for non-printable ASCII characters and a heatmap-like gradient
/// from red to yellow to white for non-ASCII bytes.
Gradient,
}
#[derive(Copy, Clone, Debug, Default, ValueEnum)]
@@ -81,14 +99,29 @@ impl Byte {
}
}
fn color(self) -> &'static [u8] {
fn color(self, color_scheme: ColorScheme) -> &'static [u8] {
use crate::ByteCategory::*;
match self.category() {
Null => COLOR_NULL.as_bytes(),
AsciiPrintable => COLOR_ASCII_PRINTABLE.as_bytes(),
AsciiWhitespace => COLOR_ASCII_WHITESPACE.as_bytes(),
AsciiOther => COLOR_ASCII_OTHER.as_bytes(),
NonAscii => COLOR_NONASCII.as_bytes(),
match color_scheme {
ColorScheme::Default => match self.category() {
Null => COLOR_NULL.as_bytes(),
AsciiPrintable => COLOR_ASCII_PRINTABLE.as_bytes(),
AsciiWhitespace => COLOR_ASCII_WHITESPACE.as_bytes(),
AsciiOther => COLOR_ASCII_OTHER.as_bytes(),
NonAscii => COLOR_NONASCII.as_bytes(),
},
ColorScheme::Gradient => match self.category() {
Null => COLOR_NULL_RGB,
AsciiWhitespace if self.0 == b' ' => &COLOR_GRADIENT_ASCII_PRINTABLE[0],
AsciiPrintable => &COLOR_GRADIENT_ASCII_PRINTABLE[(self.0 - b' ') as usize],
AsciiWhitespace | AsciiOther => {
if self.0 == 0x7f {
COLOR_DEL
} else {
&COLOR_GRADIENT_ASCII_NONPRINTABLE[self.0 as usize - 1]
}
}
NonAscii => &COLOR_GRADIENT_NONASCII[(self.0 - 128) as usize],
},
}
}
@@ -113,6 +146,37 @@ impl Byte {
},
CharacterTable::CP1047 => CP1047[self.0 as usize],
CharacterTable::CP437 => CP437[self.0 as usize],
CharacterTable::Braille => match self.category() {
// null is important enough to get its own symbol
Null => '⋄',
AsciiPrintable => self.0 as char,
AsciiWhitespace if self.0 == b' ' => ' ',
// `\t`, `\n` and `\r` are important enough to get their own symbols
AsciiWhitespace if self.0 == b'\t' => '→',
AsciiWhitespace if self.0 == b'\n' => '↵',
AsciiWhitespace if self.0 == b'\r' => '←',
AsciiWhitespace | AsciiOther | NonAscii => {
/// Adjust the bits from the original number to a new number.
///
/// Bit positions in braille are adjusted as follows:
///
/// ```text
/// 0 3 => 0 1
/// 1 4 => 2 3
/// 2 5 => 4 5
/// 6 7 => 6 7
/// ```
fn to_braille_bits(byte: u8) -> u8 {
let mut out = 0;
for (from, to) in [0, 3, 1, 4, 2, 5, 6, 7].into_iter().enumerate() {
out |= (byte >> from & 1) << to;
}
out
}
char::from_u32(0x2800 + to_braille_bits(self.0) as u32).unwrap()
}
},
}
}
}
@@ -203,6 +267,7 @@ pub struct PrinterBuilder<'a, Writer: Write> {
base: Base,
endianness: Endianness,
character_table: CharacterTable,
color_scheme: ColorScheme,
}
impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
@@ -219,6 +284,7 @@ impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
base: Base::Hexadecimal,
endianness: Endianness::Big,
character_table: CharacterTable::Default,
color_scheme: ColorScheme::Default,
}
}
@@ -272,6 +338,11 @@ impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
self
}
pub fn color_scheme(mut self, color_scheme: ColorScheme) -> Self {
self.color_scheme = color_scheme;
self
}
pub fn build(self) -> Printer<'a, Writer> {
Printer::new(
self.writer,
@@ -285,6 +356,7 @@ impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
self.base,
self.endianness,
self.character_table,
self.color_scheme,
)
}
}
@@ -298,6 +370,7 @@ pub struct Printer<'a, Writer: Write> {
show_position_panel: bool,
show_color: bool,
curr_color: Option<&'static [u8]>,
color_scheme: ColorScheme,
border_style: BorderStyle,
byte_hex_panel: Vec<String>,
byte_char_panel: Vec<String>,
@@ -329,6 +402,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
base: Base,
endianness: Endianness,
character_table: CharacterTable,
color_scheme: ColorScheme,
) -> Printer<'a, Writer> {
Printer {
idx: 0,
@@ -338,6 +412,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
show_position_panel,
show_color,
curr_color: None,
color_scheme,
border_style,
byte_hex_panel: (0u8..=u8::MAX)
.map(|i| match base {
@@ -481,9 +556,10 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
Squeezer::Print | Squeezer::Delete => self.writer.write_all(b" ")?,
Squeezer::Ignore | Squeezer::Disabled => {
if let Some(&b) = self.line_buf.get(i as usize) {
if self.show_color && self.curr_color != Some(Byte(b).color()) {
self.writer.write_all(Byte(b).color())?;
self.curr_color = Some(Byte(b).color());
if self.show_color && self.curr_color != Some(Byte(b).color(self.color_scheme))
{
self.writer.write_all(Byte(b).color(self.color_scheme))?;
self.curr_color = Some(Byte(b).color(self.color_scheme));
}
self.writer
.write_all(self.byte_char_panel[b as usize].as_bytes())?;
@@ -550,9 +626,9 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
if i % (self.group_size as usize) == 0 {
self.writer.write_all(b" ")?;
}
if self.show_color && self.curr_color != Some(Byte(b).color()) {
self.writer.write_all(Byte(b).color())?;
self.curr_color = Some(Byte(b).color());
if self.show_color && self.curr_color != Some(Byte(b).color(self.color_scheme)) {
self.writer.write_all(Byte(b).color(self.color_scheme))?;
self.curr_color = Some(Byte(b).color(self.color_scheme));
}
self.writer
.write_all(self.byte_hex_panel[b as usize].as_bytes())?;
@@ -780,6 +856,7 @@ mod tests {
Base::Hexadecimal,
Endianness::Big,
CharacterTable::Default,
ColorScheme::Default,
);
printer.print_all(input).unwrap();
@@ -836,6 +913,7 @@ mod tests {
Base::Hexadecimal,
Endianness::Big,
CharacterTable::Default,
ColorScheme::Default,
);
printer.display_offset(0xdeadbeef);
@@ -871,6 +949,7 @@ mod tests {
Base::Hexadecimal,
Endianness::Big,
CharacterTable::Default,
ColorScheme::Default,
);
printer.print_all(input).unwrap();
@@ -932,6 +1011,7 @@ mod tests {
Base::Hexadecimal,
Endianness::Big,
CharacterTable::Default,
ColorScheme::Default,
);
printer.print_all(input).unwrap();