#![forbid(unsafe_code)]
use core::fmt::{self, Write};
use super::types::Encoded;
const BAR_WIDTH: u32 = 2;
const LINEAR_QUIET: u32 = 10;
const MODULE_SIZE: usize = 4;
const MATRIX_QUIET: usize = 4 * MODULE_SIZE;
pub fn write_svg<W: Write>(encoded: Encoded, buf: &[bool], out: &mut W) -> fmt::Result {
match encoded {
Encoded::Linear { len, height } => write_linear(&buf[..len], height, out),
Encoded::Matrix { width, height } => write_matrix(&buf[..width * height], width, out),
}
}
pub fn write_linear<W: Write>(bars: &[bool], height: u32, out: &mut W) -> fmt::Result {
let total_width = bars.len() as u32 * BAR_WIDTH + 2 * LINEAR_QUIET;
let total_height = height + 2 * LINEAR_QUIET;
write!(
out,
r#"<svg xmlns="http://www.w3.org/2000/svg" width="{total_width}" height="{total_height}" viewBox="0 0 {total_width} {total_height}"><rect width="{total_width}" height="{total_height}" fill="white"/>"#,
)?;
for (i, &dark) in bars.iter().enumerate() {
if dark {
let x = LINEAR_QUIET + i as u32 * BAR_WIDTH;
write!(
out,
r#"<rect x="{x}" y="{LINEAR_QUIET}" width="{BAR_WIDTH}" height="{height}" fill="black"/>"#,
)?;
}
}
out.write_str("</svg>")
}
pub fn write_matrix<W: Write>(modules: &[bool], width: usize, out: &mut W) -> fmt::Result {
let rows = modules.len().checked_div(width).unwrap_or(0);
let px_width = width * MODULE_SIZE + 2 * MATRIX_QUIET;
let px_height = rows * MODULE_SIZE + 2 * MATRIX_QUIET;
write!(
out,
r#"<svg xmlns="http://www.w3.org/2000/svg" width="{px_width}" height="{px_height}" viewBox="0 0 {px_width} {px_height}"><rect width="{px_width}" height="{px_height}" fill="white"/>"#,
)?;
for (idx, &dark) in modules.iter().enumerate() {
if dark {
let x = MATRIX_QUIET + (idx % width) * MODULE_SIZE;
let y = MATRIX_QUIET + (idx / width) * MODULE_SIZE;
write!(
out,
r#"<rect x="{x}" y="{y}" width="{MODULE_SIZE}" height="{MODULE_SIZE}" fill="black"/>"#,
)?;
}
}
out.write_str("</svg>")
}
#[cfg(test)]
mod tests {
use super::*;
struct FixedWriter {
buf: [u8; 4096],
len: usize,
}
impl FixedWriter {
fn new() -> Self {
Self {
buf: [0; 4096],
len: 0,
}
}
fn as_str(&self) -> &str {
core::str::from_utf8(&self.buf[..self.len]).unwrap()
}
}
impl Write for FixedWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
let end = self.len + bytes.len();
if end > self.buf.len() {
return Err(fmt::Error);
}
self.buf[self.len..end].copy_from_slice(bytes);
self.len = end;
Ok(())
}
}
#[test]
fn linear_svg_no_alloc() {
let bars = [true, false, true, true, false];
let mut w = FixedWriter::new();
write_linear(&bars, 50, &mut w).unwrap();
let svg = w.as_str();
assert!(svg.starts_with("<svg "));
assert!(svg.ends_with("</svg>"));
}
#[test]
fn matrix_svg_no_alloc() {
let modules = [true, false, false, true];
let mut w = FixedWriter::new();
write_matrix(&modules, 2, &mut w).unwrap();
assert!(w.as_str().starts_with("<svg "));
}
}