use crate::api::Presentation;
use crate::generator::{SlideContent, Image};
use crate::exc::Result;
pub fn export_to_html(presentation: &Presentation) -> Result<String> {
let mut html = String::new();
html.push_str("<!DOCTYPE html>\n<html>\n<head>\n");
html.push_str("<meta charset=\"UTF-8\">\n");
html.push_str("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n");
html.push_str(&format!("<title>{}</title>\n", presentation.get_title()));
html.push_str("<style>\n");
html.push_str(include_str!("html_style.css"));
html.push_str("</style>\n");
html.push_str("</head>\n<body>\n");
html.push_str("<div class=\"slide title-slide\">\n");
html.push_str(&format!("<h1>{}</h1>\n", presentation.get_title()));
html.push_str("</div>\n");
for (i, slide) in presentation.slides().iter().enumerate() {
html.push_str(&render_slide(slide, i + 1));
}
html.push_str("</body>\n</html>");
Ok(html)
}
fn render_slide(slide: &SlideContent, index: usize) -> String {
let mut html = String::new();
html.push_str(&format!("<div class=\"slide\" id=\"slide-{}\">\n", index));
html.push_str(&format!("<div class=\"slide-number\">{}</div>\n", index));
html.push_str(&format!("<h2>{}</h2>\n", slide.title));
html.push_str("<div class=\"content\">\n");
if !slide.content.is_empty() {
html.push_str("<ul>\n");
for item in &slide.content {
html.push_str(&format!("<li>{}</li>\n", item));
}
html.push_str("</ul>\n");
}
for image in &slide.images {
if let Some(img_html) = render_image(image) {
html.push_str(&img_html);
}
}
for code in &slide.code_blocks {
html.push_str("<pre><code>");
html.push_str(&code.code);
html.push_str("</code></pre>\n");
}
html.push_str("</div>\n"); html.push_str("</div>\n");
html
}
fn render_image(image: &Image) -> Option<String> {
let bytes = image.get_bytes()?;
let b64 = base64_encode(&bytes);
let mime = match image.format.to_lowercase().as_str() {
"jpg" | "jpeg" => "image/jpeg",
"png" => "image/png",
"gif" => "image/gif",
"svg" => "image/svg+xml",
_ => "application/octet-stream",
};
Some(format!(
"<div class=\"image-container\"><img src=\"data:{};base64,{}\" alt=\"{}\" /></div>\n",
mime, b64, image.filename
))
}
fn base64_encode(data: &[u8]) -> String {
const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut output = String::with_capacity(data.len() * 4 / 3 + 4);
let mut i = 0;
while i < data.len() {
let mut buf = [0u8; 3];
let mut len = 0;
for j in 0..3 {
if i + j < data.len() {
buf[j] = data[i + j];
len += 1;
}
}
let b0 = (buf[0] >> 2) & 0x3F;
let b1 = ((buf[0] & 0x03) << 4) | ((buf[1] >> 4) & 0x0F);
let b2 = ((buf[1] & 0x0F) << 2) | ((buf[2] >> 6) & 0x03);
let b3 = buf[2] & 0x3F;
output.push(ALPHABET[b0 as usize] as char);
output.push(ALPHABET[b1 as usize] as char);
if len > 1 {
output.push(ALPHABET[b2 as usize] as char);
} else {
output.push('=');
}
if len > 2 {
output.push(ALPHABET[b3 as usize] as char);
} else {
output.push('=');
}
i += 3;
}
output
}