1use imageproc::drawing::draw_text_mut;
2use image;
3use image::{imageops, imageops::FilterType, DynamicImage, Luma, Rgba};
4use qrcode::render::svg;
5use qrcode::QrCode;
6use std::time::{SystemTime, UNIX_EPOCH};
7use std::{env, fs};
8use ab_glyph::{FontArc, PxScale};
9
10pub fn qrcode_create(text: &str, mode: &str) -> Result<String, String> {
12 let temp_dir = env::temp_dir();
13 let filename = format!("{}.{mode}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs());
14 let filepath = temp_dir.join(filename);
15
16 let code = match QrCode::new(text.as_bytes()) {
17 Ok(e) => e,
18 Err(e) => return Err(format!("Error QrCode file: {}", e)),
19 };
20 match mode {
21 "svg" => {
22 let string = code.render().min_dimensions(200, 200).dark_color(svg::Color("#000000")).light_color(svg::Color("#ffffff")).build();
23 match fs::write(filepath.clone(), string) {
24 Ok(_) => Ok(filepath.to_str().unwrap().to_string()),
25 Err(e) => Err(format!("Error writing svg file: {}", e)),
26 }
27 }
28 "png" => {
29 let image = code.render::<Luma<u8>>().build();
30 match image.save(filepath.clone()) {
31 Ok(_) => Ok(filepath.to_str().unwrap().to_string()),
32 Err(e) => Err(format!("Error writing png file: {}", e)),
33 }
34 }
35 _ => {
36 let image = code.render::<Luma<u8>>().min_dimensions(200, 200).build();
37 match image.save(filepath.clone()) {
38 Ok(_) => Ok(filepath.to_str().unwrap().to_string()),
39 Err(e) => Err(format!("Error writing file: {}", e)),
40 }
41 }
42 }
43}
44
45pub fn qrcode_read(filename: &str) -> Vec<String> {
46 let mut qrcode = vec![];
47
48 let img = image::open(filename);
49 let img_gray = match img {
50 Ok(e) => e.into_luma8(),
51 Err(_) => {
52 return qrcode;
53 }
54 };
55
56 let mut decoder = quircs::Quirc::default();
57 let codes = decoder.identify(img_gray.width() as usize, img_gray.height() as usize, &img_gray);
58 for code in codes {
59 let code = match code {
60 Ok(e) => e,
61 Err(_) => {
62 return qrcode;
63 }
64 };
65 let decoded = match code.decode() {
66 Ok(e) => e,
67 Err(_) => {
68 return qrcode;
69 }
70 };
71 qrcode.push(std::str::from_utf8(&decoded.payload.clone()).unwrap().to_string());
72 }
73 qrcode
74}
75
76pub fn create_qrcode(code: &str) -> String {
78 let code = QrCode::new(code).unwrap();
79 let string = code.render().min_dimensions(200, 200).dark_color(svg::Color("#000000")).light_color(svg::Color("#ffffff")).build();
80 string.replace("width=\"203\"", "width=\"100%\"").replace("height=\"203\"", "height=\"100%\"")
81}
82
83pub fn compose_qrcode(
85 bg_data: &[u8],
86 qrcode_data: &[u8],
87 pos_x: i64,
88 pos_y: i64,
89 size: u32,
90) -> Result<String, String> {
91 let mut bg_img = image::load_from_memory(bg_data)
92 .map_err(|e| format!("无法加载背景图: {}", e))?
93 .to_rgba8();
94
95 let mut qr_img = image::load_from_memory(qrcode_data)
96 .map_err(|e| format!("无法加载二维码: {}", e))?
97 .to_rgba8();
98
99 for pixel in qr_img.pixels_mut() {
101 if pixel[0] > 240 && pixel[1] > 240 && pixel[2] > 240 {
102 pixel[3] = 0;
103 }
104 }
105
106 let qr_resized = DynamicImage::ImageRgba8(qr_img)
107 .resize_exact(size, size, FilterType::Lanczos3);
108
109 imageops::overlay(&mut bg_img, &qr_resized, pos_x, pos_y);
110
111 let temp_dir = env::temp_dir();
113 let filename = format!("{}.png", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs());
114 let filepath = temp_dir.join(filename);
115 bg_img.save(&filepath)
116 .map_err(|e| format!("保存二维码合成图片失败: {}", e))?;
117
118 Ok(filepath.to_str().unwrap().to_string())
119}
120
121pub fn compose_text(
123 bg_data: &[u8],
124 font_data: &[u8],
125 text: &str,
126 pos_x: i32,
127 pos_y: i32,
128 font_size: f32,
129) -> Result<String, String> {
130 let mut bg_img = image::load_from_memory(bg_data)
132 .map_err(|e| format!("无法加载背景图: {}", e))?
133 .to_rgba8();
134
135 let font = FontArc::try_from_vec(font_data.to_vec())
137 .map_err(|_| "加载字体失败")?;
138
139 let scale = PxScale::from(font_size);
141
142 draw_text_mut(
144 &mut bg_img,
145 Rgba([0, 0, 0, 255]),
146 pos_x,
147 pos_y,
148 scale,
149 &font,
150 text,
151 );
152
153 let temp_dir = env::temp_dir();
155 let filename = format!("{}.png", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs());
156 let filepath = temp_dir.join(filename);
157
158 bg_img.save(&filepath)
159 .map_err(|e| format!("保存文字合成图片失败: {}", e))?;
160
161 Ok(filepath.to_str().unwrap().to_string())
162}