use crate::error::Error;
use crate::hwpx::xml_gen;
use crate::model::HwpDocument;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use zip::write::FileOptions;
use zip::CompressionMethod;
use zip::ZipWriter;
pub fn write_hwpx(doc: &HwpDocument, path: &Path) -> Result<u64, Error> {
let file = File::create(path).map_err(Error::Io)?;
let mut zip = ZipWriter::new(file);
let opts_stored = FileOptions::default().compression_method(CompressionMethod::Stored);
let opts_deflate = FileOptions::default().compression_method(CompressionMethod::Deflated);
zip.start_file("mimetype", opts_stored)
.map_err(|e| Error::Container(e.to_string()))?;
zip.write_all(&xml_gen::gen_mimetype()).map_err(Error::Io)?;
zip.start_file("version.xml", opts_deflate)
.map_err(|e| Error::Container(e.to_string()))?;
zip.write_all(xml_gen::gen_version_xml().as_bytes())
.map_err(Error::Io)?;
zip.start_file("settings.xml", opts_deflate)
.map_err(|e| Error::Container(e.to_string()))?;
zip.write_all(xml_gen::gen_settings_xml().as_bytes())
.map_err(Error::Io)?;
zip.start_file("Contents/content.hpf", opts_deflate)
.map_err(|e| Error::Container(e.to_string()))?;
zip.write_all(xml_gen::gen_content_hpf(doc).as_bytes())
.map_err(Error::Io)?;
zip.start_file("Contents/header.xml", opts_deflate)
.map_err(|e| Error::Container(e.to_string()))?;
zip.write_all(xml_gen::gen_header_xml(doc).as_bytes())
.map_err(Error::Io)?;
zip.start_file("Contents/section0.xml", opts_deflate)
.map_err(|e| Error::Container(e.to_string()))?;
zip.write_all(xml_gen::gen_section_xml(doc).as_bytes())
.map_err(Error::Io)?;
zip.start_file("META-INF/container.xml", opts_deflate)
.map_err(|e| Error::Container(e.to_string()))?;
zip.write_all(xml_gen::gen_container_xml().as_bytes())
.map_err(Error::Io)?;
zip.start_file("META-INF/manifest.xml", opts_deflate)
.map_err(|e| Error::Container(e.to_string()))?;
zip.write_all(xml_gen::gen_manifest_xml().as_bytes())
.map_err(Error::Io)?;
zip.finish().map_err(|e| Error::Container(e.to_string()))?;
let size = std::fs::metadata(path).map_err(Error::Io)?.len();
Ok(size)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_write_then_read() {
let mut doc = HwpDocument::new_for_writing(Some("Test Doc".into()));
doc.add_paragraph("Hello world", 0, 0, None);
doc.add_paragraph("Second paragraph", 0, 0, None);
let dir = std::env::temp_dir().join("hwp_mcp_test_write");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("test_roundtrip.hwpx");
write_hwpx(&doc, &path).unwrap();
let read_doc = crate::open(&path).expect("should read back the generated HWPX");
assert!(read_doc.sections[0]
.paragraphs
.iter()
.any(|p| p.contains("Hello world")));
assert!(read_doc.sections[0]
.paragraphs
.iter()
.any(|p| p.contains("Second paragraph")));
std::fs::remove_dir_all(&dir).ok();
}
#[test]
fn round_trip_with_formatting() {
let mut doc = HwpDocument::new_for_writing(None);
let bold_id = doc.ensure_char_shape(true, false, false, 14.0, 0);
doc.add_paragraph("Bold text", bold_id, 0, None);
let dir = std::env::temp_dir().join("hwp_mcp_test_fmt");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("test_fmt.hwpx");
write_hwpx(&doc, &path).unwrap();
let read_doc = crate::open(&path).unwrap();
let detail = &read_doc.sections[0].paragraph_details[0];
let csid = detail.runs.first().map(|(_, id)| *id).unwrap_or(0);
let cs = read_doc.shapes.char_shapes.get(&csid);
assert!(cs.map(|s| s.bold).unwrap_or(false));
std::fs::remove_dir_all(&dir).ok();
}
#[test]
fn round_trip_with_table() {
let mut doc = HwpDocument::new_for_writing(None);
doc.add_table(
2,
2,
Some(vec![
vec!["A1".into(), "B1".into()],
vec!["A2".into(), "B2".into()],
]),
None,
);
let dir = std::env::temp_dir().join("hwp_mcp_test_tbl");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("test_tbl.hwpx");
write_hwpx(&doc, &path).unwrap();
let read_doc = crate::open(&path).unwrap();
assert!(!read_doc.sections[0].tables.is_empty());
std::fs::remove_dir_all(&dir).ok();
}
}