use crate::qr::QrCode; pub fn render_svg(qr: &QrCode, logo: Option<&[u8]>, fg: &[u8; 3], bg: &[u8; 3]) -> String { let matrix_size = qr.size() as u32; let margin = qr.margin as u32; let total = matrix_size + 2 * margin; let fg_hex = format!("#{:02X}{:02X}{:02X}", fg[0], fg[1], fg[2]); let bg_hex = format!("#{:02X}{:02X}{:02X}", bg[0], bg[1], bg[2]); let dark_count = qr .modules() .iter() .flat_map(|row| row.iter()) .filter(|&&m| m) .count(); let mut svg = String::with_capacity(300 + dark_count * 50); svg.push_str(&format!( r#""# )); svg.push_str(&format!( r#""# )); for y in 0..matrix_size { for x in 0..matrix_size { if qr.modules()[y as usize][x as usize] { svg.push_str(&format!( r#""#, x + margin, y + margin )); } } } // Logo 嵌入:base64 PNG data URL if let Some(logo_bytes) = logo { let b64 = base64_encode(logo_bytes); let logo_size = total as f32 * 0.25; let logo_x = (total as f32 - logo_size) / 2.0; let logo_y = (total as f32 - logo_size) / 2.0; svg.push_str(&format!( r#""# )); } svg.push_str(""); svg } /// 简易 base64 编码(无外部依赖) fn base64_encode(data: &[u8]) -> String { const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let mut result = String::with_capacity(data.len().div_ceil(3) * 4); for chunk in data.chunks(3) { let b0 = chunk[0] as u32; let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 }; let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 }; let triple = (b0 << 16) | (b1 << 8) | b2; result.push(CHARS[((triple >> 18) & 0x3F) as usize] as char); result.push(CHARS[((triple >> 12) & 0x3F) as usize] as char); if chunk.len() > 1 { result.push(CHARS[((triple >> 6) & 0x3F) as usize] as char); } else { result.push('='); } if chunk.len() > 2 { result.push(CHARS[(triple & 0x3F) as usize] as char); } else { result.push('='); } } result }