mirror of
https://framagit.org/almet/jardiflore.git
synced 2025-04-28 12:02:37 +02:00
Generate images directly from the program
This commit is contained in:
parent
12f116c5d8
commit
c6aff4e2d7
3 changed files with 1584 additions and 15 deletions
1458
Cargo.lock
generated
1458
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -8,5 +8,11 @@ name = "jardiflore"
|
||||||
path = "main.rs"
|
path = "main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ab_glyph = "0.2.29"
|
||||||
calamine = "0.26.1"
|
calamine = "0.26.1"
|
||||||
|
chrono = "0.4.40"
|
||||||
|
image = "0.25.6"
|
||||||
|
imageproc = "0.25.0"
|
||||||
serde = "1.0.217"
|
serde = "1.0.217"
|
||||||
|
small_uid = "0.2.4"
|
||||||
|
structopt = "0.3.26"
|
||||||
|
|
135
main.rs
135
main.rs
|
@ -1,5 +1,40 @@
|
||||||
use calamine::{open_workbook, Data, Reader, Xlsx};
|
use calamine::{open_workbook, Data, Reader, Xlsx};
|
||||||
|
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use ab_glyph::{FontRef, PxScale};
|
||||||
|
use image::{ImageBuffer, Rgba};
|
||||||
|
use imageproc::drawing::draw_text_mut;
|
||||||
|
use small_uid::SmallUid;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
// Configuration for the label
|
||||||
|
const DPI: f32 = 300.0;
|
||||||
|
const MM_TO_PX: f32 = DPI / 25.4;
|
||||||
|
const LABEL_WIDTH: f32 = 210.0 * MM_TO_PX;
|
||||||
|
const LABEL_HEIGHT: f32 = 20.0 * MM_TO_PX;
|
||||||
|
|
||||||
|
// Elements positionning
|
||||||
|
const LOGO_WIDTH: u32 = 151;
|
||||||
|
const LOGO_HEIGHT: u32 = 64;
|
||||||
|
const LOGO_POS_X: u32 = LABEL_WIDTH as u32 - LOGO_WIDTH - 10;
|
||||||
|
const LOGO_POS_Y: u32 = 10;
|
||||||
|
|
||||||
|
const CLIENT_CODE_POS_X: i32 = 10;
|
||||||
|
const CLIENT_CODE_POS_Y: i32 = 10;
|
||||||
|
const CLIENT_CODE_SCALE: f32 = 24.0;
|
||||||
|
|
||||||
|
const DATE_POS_X: i32 = 10;
|
||||||
|
const DATE_POS_Y: i32 = 40;
|
||||||
|
const DATE_SCALE: f32 = 32.0;
|
||||||
|
|
||||||
|
const SPECIES_VARIETY_POS_X: i32 = 10;
|
||||||
|
const SPECIES_VARIETY_POS_Y: i32 = 80;
|
||||||
|
const SPECIES_VARIETY_SCALE: f32 = 24.0;
|
||||||
|
|
||||||
|
const COLOR_TEXT: Rgba<u8> = Rgba([0, 0, 0, 255]);
|
||||||
|
const COLOR_BACKGROUND: Rgba<u8> = Rgba([255, 255, 255, 255]);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Record {
|
struct Record {
|
||||||
client_code: String,
|
client_code: String,
|
||||||
|
@ -62,10 +97,102 @@ fn parse_xlsx(delivery_week_filter: &str) -> Result<Vec<Record>, Box<dyn std::er
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
// The label looks like this:
|
||||||
let records = parse_xlsx("s06")?;
|
//
|
||||||
for record in records {
|
// Tomate (Solanum lycopersicum) (species) /^\
|
||||||
println!("{:?}", record);
|
// Variété: San Marzano (variety) / j \
|
||||||
|
// / ard \
|
||||||
|
// / iflore\
|
||||||
|
// ---------
|
||||||
|
// SEMAINE DE LIVRAISON: 22
|
||||||
|
//
|
||||||
|
// 123 Allée des Récoltes
|
||||||
|
// Tél: Téléphone.
|
||||||
|
//
|
||||||
|
fn generate_label(output_dir: &String, record: Record) -> PathBuf {
|
||||||
|
let font_data = include_bytes!("./assets/DejaVuSans.ttf");
|
||||||
|
let font = FontRef::try_from_slice(font_data).unwrap();
|
||||||
|
let logo = image::open("./assets/logo.png").unwrap();
|
||||||
|
|
||||||
|
let mut label = ImageBuffer::new(LABEL_WIDTH as u32, LABEL_HEIGHT as u32);
|
||||||
|
|
||||||
|
// Fill in the background
|
||||||
|
for pixel in label.pixels_mut() {
|
||||||
|
*pixel = COLOR_BACKGROUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logo
|
||||||
|
let resized_logo = image::imageops::resize(
|
||||||
|
&logo,
|
||||||
|
LOGO_WIDTH,
|
||||||
|
LOGO_HEIGHT,
|
||||||
|
image::imageops::FilterType::Lanczos3,
|
||||||
|
);
|
||||||
|
|
||||||
|
image::imageops::overlay(
|
||||||
|
&mut label,
|
||||||
|
&resized_logo,
|
||||||
|
LOGO_POS_X as i64,
|
||||||
|
LOGO_POS_Y as i64,
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_text_mut(
|
||||||
|
&mut label,
|
||||||
|
COLOR_TEXT,
|
||||||
|
CLIENT_CODE_POS_X,
|
||||||
|
CLIENT_CODE_POS_Y,
|
||||||
|
PxScale::from(CLIENT_CODE_SCALE),
|
||||||
|
&font,
|
||||||
|
&record.client_code,
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_text_mut(
|
||||||
|
&mut label,
|
||||||
|
COLOR_TEXT,
|
||||||
|
DATE_POS_X,
|
||||||
|
DATE_POS_Y,
|
||||||
|
PxScale::from(DATE_SCALE),
|
||||||
|
&font,
|
||||||
|
&record.delivery_week,
|
||||||
|
);
|
||||||
|
|
||||||
|
let species_varieties: String = "".to_owned() + &record.species + " " + &record.variety;
|
||||||
|
draw_text_mut(
|
||||||
|
&mut label,
|
||||||
|
COLOR_TEXT,
|
||||||
|
SPECIES_VARIETY_POS_X,
|
||||||
|
SPECIES_VARIETY_POS_Y,
|
||||||
|
PxScale::from(SPECIES_VARIETY_SCALE),
|
||||||
|
&font,
|
||||||
|
&species_varieties,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Save the label
|
||||||
|
|
||||||
|
let output_path = Path::new(output_dir).join(format!("label_{}.png", SmallUid::new()));
|
||||||
|
label.save(output_path.clone()).unwrap();
|
||||||
|
|
||||||
|
return output_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
struct Opts {
|
||||||
|
#[structopt(short = "o", long, default_value = "output")]
|
||||||
|
output_dir: String,
|
||||||
|
#[structopt(short = "w", long, default_value = "06")]
|
||||||
|
week: String,
|
||||||
|
}
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let opts = Opts::from_args();
|
||||||
|
std::fs::create_dir_all(&opts.output_dir).unwrap();
|
||||||
|
|
||||||
|
let week_number = "s".to_owned() + &opts.week;
|
||||||
|
let records = parse_xlsx(&week_number)?;
|
||||||
|
|
||||||
|
for record in records {
|
||||||
|
let path = generate_label(&opts.output_dir, record);
|
||||||
|
println!("{}", path.into_os_string().into_string().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue