feat(main): very early version of image output
This commit is contained in:
parent
bad28abf05
commit
a0ad592374
7 changed files with 1092 additions and 21 deletions
984
Cargo.lock
generated
984
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -4,11 +4,14 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ab_glyph = "0.2.30"
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.98"
|
||||||
clap = { version = "4.5.40", features = ["derive"] }
|
clap = { version = "4.5.40", features = ["derive"] }
|
||||||
console = "0.16.0"
|
console = "0.16.0"
|
||||||
csv = "1.3.1"
|
csv = "1.3.1"
|
||||||
dialoguer = "0.11.0"
|
dialoguer = "0.11.0"
|
||||||
|
image = "0.25.6"
|
||||||
|
imageproc = "0.25.0"
|
||||||
indicatif = "0.18.0"
|
indicatif = "0.18.0"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
|
|
BIN
font/Minecraft-Bold.ttf
Normal file
BIN
font/Minecraft-Bold.ttf
Normal file
Binary file not shown.
BIN
font/Minecraft-BoldItalic.ttf
Normal file
BIN
font/Minecraft-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
font/Minecraft-Italic.ttf
Normal file
BIN
font/Minecraft-Italic.ttf
Normal file
Binary file not shown.
BIN
font/Minecraft-Regular.ttf
Normal file
BIN
font/Minecraft-Regular.ttf
Normal file
Binary file not shown.
116
src/main.rs
116
src/main.rs
|
@ -1,16 +1,20 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::Write,
|
io::Write,
|
||||||
|
path::Path,
|
||||||
process::exit,
|
process::exit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use ab_glyph::{FontRef, PxScale};
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use csv::Writer;
|
use csv::Writer;
|
||||||
use dialoguer::MultiSelect;
|
use dialoguer::MultiSelect;
|
||||||
|
use image::{Rgb, RgbImage};
|
||||||
|
use imageproc::drawing::{draw_text_mut, text_size};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rayon::{
|
use rayon::{
|
||||||
iter::{IntoParallelIterator, ParallelIterator},
|
iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator},
|
||||||
str::ParallelString,
|
str::ParallelString,
|
||||||
};
|
};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
@ -89,7 +93,7 @@ fn main() -> Result<()> {
|
||||||
.interact()?;
|
.interact()?;
|
||||||
|
|
||||||
match &args.format {
|
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::CSV => save_csv_file(&args.output, extracted, selection)?,
|
||||||
OutputFormat::TXT => save_txt_file(&args.output, extracted, selection)?,
|
OutputFormat::TXT => save_txt_file(&args.output, extracted, selection)?,
|
||||||
}
|
}
|
||||||
|
@ -97,16 +101,88 @@ fn main() -> Result<()> {
|
||||||
Ok(())
|
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(
|
fn save_txt_file(
|
||||||
output: &String,
|
output: &String,
|
||||||
extracted: Vec<String>,
|
extracted: Vec<String>,
|
||||||
selection: Vec<usize>,
|
selection: Vec<usize>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut out_file = File::create(output)?;
|
let mut out_file = File::create(output)?;
|
||||||
Ok(for msg in selection {
|
|
||||||
|
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(extracted[msg].as_bytes())?;
|
||||||
out_file.write(b"\n")?;
|
out_file.write(b"\n")?;
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_csv_file(
|
fn save_csv_file(
|
||||||
|
@ -117,26 +193,42 @@ fn save_csv_file(
|
||||||
let mut out_file = Writer::from_path(output)?;
|
let mut out_file = Writer::from_path(output)?;
|
||||||
out_file.write_record(&["date", "time", "msg"])?;
|
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
|
let time: Vec<&str> = EXTRACT_TIME
|
||||||
.captures(&extracted[msg])
|
.captures(&msg)
|
||||||
.expect("Unable to extract time")
|
.expect("Unable to extract time")
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str()
|
.as_str()
|
||||||
.strip_prefix("[").expect("Unable to remove time prefix")
|
.strip_prefix("[")
|
||||||
.strip_suffix("]").expect("Unable to remove time suffix")
|
.expect("Unable to remove time prefix")
|
||||||
.split(' ').collect();
|
.strip_suffix("]")
|
||||||
|
.expect("Unable to remove time suffix")
|
||||||
|
.split(' ')
|
||||||
|
.collect();
|
||||||
out_file.write_record(&[
|
out_file.write_record(&[
|
||||||
time[0],
|
time[0],
|
||||||
time[1],
|
time[1],
|
||||||
EXTRACT_MSG
|
EXTRACT_MSG
|
||||||
.captures(&extracted[msg])
|
.captures(&msg)
|
||||||
.expect("Unable to extract time")
|
.expect("Unable to extract time")
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str()])?;
|
.as_str(),
|
||||||
})
|
])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_possible_chat_msg(input: &str) -> bool {
|
fn is_possible_chat_msg(input: &str) -> bool {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue