feat(main): very early version of image output

This commit is contained in:
aria 2025-07-07 23:16:13 +10:00
parent bad28abf05
commit a0ad592374
Signed by: aria
SSH key fingerprint: SHA256:WqtcVnDMrv1lnUlNah5k31iywFUI/DV+5yHzCTO4Vds
7 changed files with 1092 additions and 21 deletions

984
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,11 +4,14 @@ version = "0.1.0"
edition = "2024"
[dependencies]
ab_glyph = "0.2.30"
anyhow = "1.0.98"
clap = { version = "4.5.40", features = ["derive"] }
console = "0.16.0"
csv = "1.3.1"
dialoguer = "0.11.0"
image = "0.25.6"
imageproc = "0.25.0"
indicatif = "0.18.0"
once_cell = "1.21.3"
rayon = "1.10.0"

BIN
font/Minecraft-Bold.ttf Normal file

Binary file not shown.

Binary file not shown.

BIN
font/Minecraft-Italic.ttf Normal file

Binary file not shown.

BIN
font/Minecraft-Regular.ttf Normal file

Binary file not shown.

View file

@ -1,16 +1,20 @@
use std::{
fs::{self, File},
io::Write,
path::Path,
process::exit,
};
use ab_glyph::{FontRef, PxScale};
use anyhow::{Error, Result};
use clap::Parser;
use csv::Writer;
use dialoguer::MultiSelect;
use image::{Rgb, RgbImage};
use imageproc::drawing::{draw_text_mut, text_size};
use once_cell::sync::Lazy;
use rayon::{
iter::{IntoParallelIterator, ParallelIterator},
iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator},
str::ParallelString,
};
use regex::Regex;
@ -89,7 +93,7 @@ fn main() -> Result<()> {
.interact()?;
match &args.format {
OutputFormat::Image => todo!(),
OutputFormat::Image => save_image_file(&args.output, extracted, selection)?,
OutputFormat::CSV => save_csv_file(&args.output, extracted, selection)?,
OutputFormat::TXT => save_txt_file(&args.output, extracted, selection)?,
}
@ -97,16 +101,88 @@ fn main() -> Result<()> {
Ok(())
}
fn save_image_file(
output: &String,
extracted: Vec<String>,
selection: Vec<usize>,
) -> Result<(), Error> {
let out_path = Path::new(output);
let font = FontRef::try_from_slice(include_bytes!("../font/Minecraft-Regular.ttf")).unwrap();
let height = 40.0;
let scale = PxScale {
x: height * 2.0,
y: height,
};
let mut selected: Vec<String> = vec![];
if selection.is_empty() {
selected = extracted;
} else {
for x in selection {
selected.push(extracted[x].clone());
}
}
let mut longest: String = selected.first().expect("selected list is empty?").clone();
for msg in &selected {
if msg.len() > longest.len() {
longest = msg.clone();
}
}
// let text = extracted[selection[0]].clone();
let (w, h) = text_size(scale, &font, &longest);
println!("Text size: {}x{}", w, h);
let image_width = w + 8;
let image_height = (h * selected.len() as u32) + (4 * selected.len() as u32) + 4;
let mut image = RgbImage::new(image_width, image_height);
let mut current_line: u32 = 0;
for msg in &selected {
draw_text_mut(
&mut image,
Rgb([254u8, 254u8, 254u8]),
4,
((current_line * h) + (4 * current_line))
.try_into()
.unwrap(),
scale,
&font,
&msg,
);
current_line += 1;
}
Ok(image.save(out_path).unwrap())
}
fn save_txt_file(
output: &String,
extracted: Vec<String>,
selection: Vec<usize>,
) -> Result<(), Error> {
let mut out_file = File::create(output)?;
Ok(for msg in selection {
out_file.write(extracted[msg].as_bytes())?;
out_file.write(b"\n")?;
})
if selection.is_empty() {
for msg in extracted {
out_file.write(msg.as_bytes())?;
out_file.write(b"\n")?;
}
} else {
for msg in selection {
out_file.write(extracted[msg].as_bytes())?;
out_file.write(b"\n")?;
}
}
Ok(())
}
fn save_csv_file(
@ -117,26 +193,42 @@ fn save_csv_file(
let mut out_file = Writer::from_path(output)?;
out_file.write_record(&["date", "time", "msg"])?;
Ok(for msg in selection {
let mut selected: Vec<String> = vec![];
if selection.is_empty() {
selected = extracted;
} else {
for x in selection {
selected.push(extracted[x].clone());
}
}
for msg in selected {
let time: Vec<&str> = EXTRACT_TIME
.captures(&extracted[msg])
.captures(&msg)
.expect("Unable to extract time")
.get(0)
.unwrap()
.as_str()
.strip_prefix("[").expect("Unable to remove time prefix")
.strip_suffix("]").expect("Unable to remove time suffix")
.split(' ').collect();
.strip_prefix("[")
.expect("Unable to remove time prefix")
.strip_suffix("]")
.expect("Unable to remove time suffix")
.split(' ')
.collect();
out_file.write_record(&[
time[0],
time[1],
EXTRACT_MSG
.captures(&extracted[msg])
.expect("Unable to extract time")
.get(0)
.unwrap()
.as_str()])?;
})
.captures(&msg)
.expect("Unable to extract time")
.get(0)
.unwrap()
.as_str(),
])?;
}
Ok(())
}
fn is_possible_chat_msg(input: &str) -> bool {