Generate actual images
This commit is contained in:
1186
Cargo.lock
generated
1186
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,9 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.8.4"
|
axum = "0.8.4"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
|
image = "0.25.8"
|
||||||
serde = { version = "1.0.227", features = ["derive"] }
|
serde = { version = "1.0.227", features = ["derive"] }
|
||||||
|
text-to-png = "0.2.0"
|
||||||
tokio = { version = "1.47.1", features = ["fs", "rt-multi-thread"] }
|
tokio = { version = "1.47.1", features = ["fs", "rt-multi-thread"] }
|
||||||
tokio-util = { version = "0.7.16", features = ["io"] }
|
tokio-util = { version = "0.7.16", features = ["io"] }
|
||||||
tower-http = { version = "0.6.6", features = ["fs", "trace"] }
|
tower-http = { version = "0.6.6", features = ["fs", "trace"] }
|
||||||
|
|||||||
@@ -233,7 +233,8 @@
|
|||||||
|
|
||||||
const formData = {
|
const formData = {
|
||||||
count: parseInt(form.count.value),
|
count: parseInt(form.count.value),
|
||||||
dimensions: `${form.width.value}x${form.height.value}`,
|
width: Number(form.width.value),
|
||||||
|
height: Number(form.height.value),
|
||||||
filenames: form.filenames.value
|
filenames: form.filenames.value
|
||||||
.trim()
|
.trim()
|
||||||
.split("\n")
|
.split("\n")
|
||||||
|
|||||||
72
src/main.rs
72
src/main.rs
@@ -1,12 +1,12 @@
|
|||||||
use std::io::{Cursor, Write};
|
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
Json, Router,
|
Json, Router,
|
||||||
body::Body,
|
body::Body,
|
||||||
http::{Response, StatusCode, header},
|
http::{Response, StatusCode, header},
|
||||||
routing::post,
|
routing::post,
|
||||||
};
|
};
|
||||||
|
use image::{GenericImage, GenericImageView};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::io::{Cursor, Write};
|
||||||
use tower_http::services::ServeFile;
|
use tower_http::services::ServeFile;
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
@@ -29,41 +29,34 @@ async fn main() {
|
|||||||
pub struct GenerateOptions {
|
pub struct GenerateOptions {
|
||||||
count: u32,
|
count: u32,
|
||||||
filenames: Vec<String>,
|
filenames: Vec<String>,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn download_zip(Json(payload): Json<GenerateOptions>) -> Response<Body> {
|
async fn download_zip(Json(payload): Json<GenerateOptions>) -> Response<Body> {
|
||||||
println!(
|
println!(
|
||||||
"Count: {}.\nFiles: ' [ {} ]'",
|
"Count: {}. w: {}, h: {}, \nFiles: ' [ {} ]'",
|
||||||
payload.count,
|
payload.count,
|
||||||
|
payload.width,
|
||||||
|
payload.height,
|
||||||
payload.filenames.join(",")
|
payload.filenames.join(",")
|
||||||
);
|
);
|
||||||
|
let files = get_files(payload);
|
||||||
// Example: Simulate multiple files in memory
|
|
||||||
let files: Vec<(&str, &[u8])> = vec![
|
|
||||||
("hello.txt", b"Hello, world!"),
|
|
||||||
("readme.md", b"# README\nThis is a test."),
|
|
||||||
("data.json", br#"{"key": "value"}"#),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Create a buffer to hold the ZIP file in memory
|
|
||||||
let mut buffer = Cursor::new(Vec::new());
|
let mut buffer = Cursor::new(Vec::new());
|
||||||
|
|
||||||
{
|
let mut zip = zip::ZipWriter::new(&mut buffer);
|
||||||
// Create ZIP writer over the buffer
|
|
||||||
let mut zip = zip::ZipWriter::new(&mut buffer);
|
|
||||||
|
|
||||||
let options = zip::write::SimpleFileOptions::default()
|
let options = zip::write::SimpleFileOptions::default()
|
||||||
.compression_method(zip::CompressionMethod::Stored)
|
.compression_method(zip::CompressionMethod::Stored)
|
||||||
.unix_permissions(0o644);
|
.unix_permissions(0o644);
|
||||||
|
|
||||||
for (filename, contents) in files {
|
for (filename, contents) in files {
|
||||||
zip.start_file(filename, options).unwrap();
|
zip.start_file(filename, options).unwrap();
|
||||||
zip.write_all(contents).unwrap();
|
zip.write_all(&contents).unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
zip.finish().unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zip.finish().unwrap();
|
||||||
|
|
||||||
let zip_bytes = buffer.into_inner();
|
let zip_bytes = buffer.into_inner();
|
||||||
|
|
||||||
Response::builder()
|
Response::builder()
|
||||||
@@ -76,3 +69,34 @@ async fn download_zip(Json(payload): Json<GenerateOptions>) -> Response<Body> {
|
|||||||
.body(Body::from(zip_bytes))
|
.body(Body::from(zip_bytes))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_files(options: GenerateOptions) -> Vec<(String, Vec<u8>)> {
|
||||||
|
let mut files: Vec<(String, Vec<u8>)> = Vec::new();
|
||||||
|
use text_to_png::TextRenderer;
|
||||||
|
let renderer = TextRenderer::default();
|
||||||
|
|
||||||
|
for f in options.filenames {
|
||||||
|
let mut imgbuf = image::ImageBuffer::new(options.width, options.height);
|
||||||
|
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
|
||||||
|
*pixel = image::Rgba([255, 0, 2, 255]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let textpngdata = renderer
|
||||||
|
.render_text_to_png_data(&f, 64, "Dark Turquoise")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let other = image::ImageReader::new(Cursor::new(textpngdata.data))
|
||||||
|
.with_guessed_format()
|
||||||
|
.unwrap()
|
||||||
|
.decode()
|
||||||
|
.unwrap();
|
||||||
|
imgbuf.copy_from(&other, 0, 0).unwrap();
|
||||||
|
|
||||||
|
let mut buf = Cursor::new(Vec::new());
|
||||||
|
imgbuf.write_to(&mut buf, image::ImageFormat::Png).unwrap();
|
||||||
|
|
||||||
|
files.push((String::from(format!("{}.png", f)), buf.into_inner()));
|
||||||
|
}
|
||||||
|
|
||||||
|
files
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user