use std::collections::HashMap;
use std::fs::File;
use std::io::{Cursor, Read, Write};
use quick_xml::events::{BytesText, Event};
use quick_xml::Writer;
use serde_json::Value;
use zip::ZipArchive;
use crate::utils::*;
pub const PLACEHOLDER_START: &str = "{{";
pub const PLACEHOLDER_END: &str = "}}";
pub static WORD_PARAGRAPH_TAG: &[u8] = b"w:p";
pub const PLACEHOLDER_BLOCK_START: &str = "{%";
pub const PLACEHOLDER_BLOCK_END: &str = "%}";
pub struct DOCX {
pub content: HashMap<String, String>,
pub file_name: String,
pub placeholders: HashMap<String, String>,
pub placeholders_blocks: HashMap<String, String>,
pub placeholder_images: HashMap<String, String>,
}
impl DOCX {
pub fn new(file_name: String) -> DOCX {
DOCX {
file_name,
content: HashMap::new(),
placeholders: HashMap::new(),
placeholders_blocks: HashMap::new(),
placeholder_images: HashMap::new(),
}
}
pub fn read(&mut self) {
self.content = read_raw_docx(&self.file_name);
}
pub fn add_placeholder(&mut self, placeholder: &str, replaced_content: &str) {
self.placeholders.insert(placeholder.to_string(), replaced_content.to_owned());
}
pub fn add_placeholders_from_json(&mut self, placeholders_content: &str) {
let v: Value = serde_json::from_str(placeholders_content).unwrap();
if let Value::Object(map) = v {
process_json_map(self,"", &map);
}
}
pub fn add_image_placeholder(&mut self, placeholder: &str, image: &str) {
let media_dir = "word/media/";
self.placeholder_images.insert(format!("{}{}", media_dir, placeholder.to_string()), image.to_string());
}
pub fn remove_image_placeholder(&mut self, placeholder: &str) {
let media_dir = "word/media/";
self.placeholder_images.remove(&format!("{}{}", media_dir, placeholder));
}
pub fn init_placeholders(&mut self) {
let mut new_content = HashMap::new();
for (k, v) in &self.content {
let mut xml_writer = Writer::new(Cursor::new(Vec::new()));
let mut reader = quick_xml::Reader::from_str(v);
reader.trim_text(false);
let mut buf = Vec::new();
let mut current_placeholder = String::new();
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(e)) => {
xml_writer.write_event(Event::Start(e)).unwrap();
}
Ok(Event::Text(e)) => {
let mut text = e.unescape().unwrap().into_owned();
if !current_placeholder.is_empty() || text.contains(PLACEHOLDER_START) {
current_placeholder.push_str(&text);
if current_placeholder.contains(PLACEHOLDER_START) && current_placeholder.contains(PLACEHOLDER_END) {
process_text(&self, &mut current_placeholder);
xml_writer.write_event(Event::Text(BytesText::new(¤t_placeholder))).unwrap();
current_placeholder.clear();
}
} else {
process_text(&self, &mut text);
xml_writer.write_event(Event::Text(BytesText::new(&text))).unwrap();
}
}
Ok(Event::End(e)) => {
xml_writer.write_event(Event::End(e)).unwrap();
}
Ok(Event::Empty(e)) => {
xml_writer.write_event(Event::Empty(e)).unwrap();
}
Ok(Event::Eof) => break,
Ok(e) => {
xml_writer.write_event(e).unwrap();
}
Err(_) => break,
}
buf.clear();
}
let result = xml_writer.into_inner().into_inner();
let result_str = String::from_utf8(result).unwrap();
new_content.insert(k.clone(), result_str);
}
self.content = new_content;
}
pub fn save(&self, output: &str) {
use zip::{write::FileOptions, ZipWriter};
let media_dir = "word/media/";
let input_file = File::open(&self.file_name).unwrap();
let mut archive = ZipArchive::new(input_file).unwrap();
let output_file = File::create(output).unwrap();
let mut zip = ZipWriter::new(output_file);
let options = FileOptions::default();
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let name = file.name().to_string();
if self.content.contains_key(&name) {
zip.start_file(&name, options).unwrap();
zip.write_all(self.content.get(&name).unwrap().as_bytes())
.unwrap();
} else if name.starts_with(media_dir) && self.placeholder_images.contains_key(&name)
&& name.ends_with(".jpeg") || name.ends_with(".png") {
let mut file = File::open(self.placeholder_images.get(&name).unwrap()).unwrap();
let mut file_data = Vec::new();
file.read_to_end(&mut file_data).unwrap();
zip.start_file(&name, options).unwrap();
zip.write_all(&*file_data).expect("panic!");
}
else {
zip.start_file(&name, options).unwrap();
std::io::copy(&mut file, &mut zip).unwrap();
}
}
zip.finish().unwrap();
}
}