use crate::font::{GlyphData, PathCommand};
use std::fmt::Write;
pub fn render_svg(
glyphs: &[GlyphData],
jittered_commands: &[Vec<PathCommand>],
font_size: u32,
units_per_em: u16,
) -> String {
let scale = font_size as f64 / units_per_em as f64;
let total_advance: f64 = glyphs.iter().map(|g| g.advance_width as f64).sum();
let svg_width = (total_advance * scale).ceil() as u32;
let svg_height = (font_size as f64 * 1.5).ceil() as u32;
let baseline_y = font_size as f64 * 1.1;
let mut svg = String::new();
writeln!(
&mut svg,
r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {svg_width} {svg_height}" width="{svg_width}" height="{svg_height}">"#,
)
.unwrap();
let mut cursor_x: f64 = 0.0;
for (i, (glyph, commands)) in glyphs.iter().zip(jittered_commands.iter()).enumerate() {
if commands.is_empty() {
cursor_x += glyph.advance_width as f64;
continue;
}
let path_d = commands_to_path_d(commands, scale, cursor_x * scale, baseline_y);
if !path_d.is_empty() {
writeln!(&mut svg, r#" <path id="g{i}" d="{path_d}" fill="black"/>"#,).unwrap();
}
cursor_x += glyph.advance_width as f64;
}
writeln!(&mut svg, "</svg>").unwrap();
svg
}
fn commands_to_path_d(
commands: &[PathCommand],
scale: f64,
offset_x: f64,
baseline_y: f64,
) -> String {
let mut d = String::new();
for cmd in commands {
match cmd {
PathCommand::MoveTo(x, y) => {
let sx = *x as f64 * scale + offset_x;
let sy = -*y as f64 * scale + baseline_y;
write!(&mut d, "M{sx:.2} {sy:.2} ").unwrap();
}
PathCommand::LineTo(x, y) => {
let sx = *x as f64 * scale + offset_x;
let sy = -*y as f64 * scale + baseline_y;
write!(&mut d, "L{sx:.2} {sy:.2} ").unwrap();
}
PathCommand::QuadTo(cx, cy, x, y) => {
let scx = *cx as f64 * scale + offset_x;
let scy = -*cy as f64 * scale + baseline_y;
let sx = *x as f64 * scale + offset_x;
let sy = -*y as f64 * scale + baseline_y;
write!(&mut d, "Q{scx:.2} {scy:.2} {sx:.2} {sy:.2} ").unwrap();
}
PathCommand::CurveTo(cx0, cy0, cx1, cy1, x, y) => {
let scx0 = *cx0 as f64 * scale + offset_x;
let scy0 = -*cy0 as f64 * scale + baseline_y;
let scx1 = *cx1 as f64 * scale + offset_x;
let scy1 = -*cy1 as f64 * scale + baseline_y;
let sx = *x as f64 * scale + offset_x;
let sy = -*y as f64 * scale + baseline_y;
write!(
&mut d,
"C{scx0:.2} {scy0:.2} {scx1:.2} {scy1:.2} {sx:.2} {sy:.2} "
)
.unwrap();
}
PathCommand::Close => {
write!(&mut d, "Z ").unwrap();
}
}
}
d.trim_end().to_string()
}