#[allow(warnings)]
#[cfg(feature = "docx")]
pub mod docx {
use std::{fs::File, io::Read, path::PathBuf};
use zip::ZipArchive;
pub fn docx_replace<T>(
docx_path: PathBuf,
target: T,
new_target: T,
) -> Result<(), Box<dyn std::error::Error>>
where
T: ToString,
{
use std::fs::File;
use std::io::prelude::*;
use zip::read::ZipArchive;
use zip::write::FileOptions;
use zip::CompressionMethod;
let file = File::open(&docx_path)?;
let mut archive = ZipArchive::new(file)?;
let new_archive_path = "new_archive.zip";
let options = FileOptions::default()
.compression_method(CompressionMethod::Stored)
.unix_permissions(0o755);
let file = File::create(&new_archive_path).unwrap();
let mut new_zip = zip::ZipWriter::new(file);
for i in 0..archive.len() {
let mut file_in_archive = archive.by_index(i).unwrap();
let file_in_archive_name = &file_in_archive.name();
if file_in_archive_name.to_string() == "word/document.xml" {
let mut contents = String::new();
file_in_archive.read_to_string(&mut contents).unwrap();
let new_contents = contents.replace(&target.to_string(), &new_target.to_string());
new_zip.start_file("word/document.xml", options)?;
new_zip.write_all(new_contents.as_bytes())?;
} else {
new_zip.start_file(file_in_archive_name.to_string(), options)?;
let mut buffer = Vec::new();
file_in_archive.read_to_end(&mut buffer)?;
new_zip.write_all(&buffer)?;
}
}
std::fs::rename(new_archive_path, docx_path).unwrap();
Ok(())
}
pub fn docx_bytes_replace_values<T>(
docx_bytes: Vec<u8>,
values: Vec<(T, T)>,
) -> Result<Vec<u8>, Box<dyn std::error::Error>>
where
T: ToString,
{
use std::fs::File;
use std::io::{Read, Write, Cursor};
use std::path::PathBuf;
use zip::read::ZipArchive;
use zip::write::FileOptions;
use zip::CompressionMethod;
let file = Cursor::new(docx_bytes);
let mut archive = ZipArchive::new(file)?;
let options = FileOptions::default()
.compression_method(CompressionMethod::Stored)
.unix_permissions(0o755);
let mut new_zip_data = Cursor::new(Vec::new());
let mut new_zip = zip::ZipWriter::new(&mut new_zip_data);
for i in 0..archive.len() {
let mut file_in_archive = archive.by_index(i)?;
let file_in_archive_name = file_in_archive.name().to_string();
if file_in_archive_name.ends_with(".xml") {
let mut contents = String::new();
file_in_archive.read_to_string(&mut contents)?;
let mut new_contents = contents;
for (target, new_target) in &values {
new_contents = new_contents.replace(&target.to_string(), &new_target.to_string());
}
new_zip.start_file(&file_in_archive_name, options)?;
new_zip.write_all(new_contents.as_bytes())?;
} else {
new_zip.start_file(&file_in_archive_name, options)?;
let mut buffer = Vec::new();
file_in_archive.read_to_end(&mut buffer)?;
new_zip.write_all(&buffer)?;
}
}
new_zip.finish()?;
drop(new_zip);
Ok(new_zip_data.into_inner().to_vec())
}
pub fn docx_replace_values<T>(
docx_path: PathBuf,
values: Vec<(T, T)>,
) -> Result<(), Box<dyn std::error::Error>>
where
T: ToString,
{
use std::fs::File;
use std::io::{Read, Write, Cursor};
use std::path::PathBuf;
use zip::read::ZipArchive;
use zip::write::FileOptions;
use zip::CompressionMethod;
let file = File::open(&docx_path)?;
let mut archive = ZipArchive::new(file)?;
let options = FileOptions::default()
.compression_method(CompressionMethod::Stored)
.unix_permissions(0o755);
let mut new_zip_data = Cursor::new(Vec::new());
let mut new_zip = zip::ZipWriter::new(&mut new_zip_data);
for i in 0..archive.len() {
let mut file_in_archive = archive.by_index(i)?;
let file_in_archive_name = file_in_archive.name().to_string();
if file_in_archive_name.ends_with(".xml") {
let mut contents = String::new();
file_in_archive.read_to_string(&mut contents)?;
let mut new_contents = contents;
for (target, new_target) in &values {
new_contents = new_contents.replace(&target.to_string(), &new_target.to_string());
}
new_zip.start_file(&file_in_archive_name, options)?;
new_zip.write_all(new_contents.as_bytes())?;
} else {
new_zip.start_file(&file_in_archive_name, options)?;
let mut buffer = Vec::new();
file_in_archive.read_to_end(&mut buffer)?;
new_zip.write_all(&buffer)?;
}
}
new_zip.finish()?;
drop(new_zip);
let mut file = File::create(&docx_path)?;
file.write_all(&new_zip_data.into_inner())?;
Ok(())
}
pub fn docx_replace_values_old<T>(
docx_path: PathBuf,
values: Vec<(T, T)>,
) -> Result<(), Box<dyn std::error::Error>>
where
T: ToString,
{
use std::fs::File;
use std::io::prelude::*;
use zip::read::ZipArchive;
use zip::write::FileOptions;
use zip::CompressionMethod;
let file = File::open(docx_path.clone())?;
let mut archive = ZipArchive::new(file)?;
let new_archive_path = "new_archive.zip";
let options = FileOptions::default()
.compression_method(CompressionMethod::Stored)
.unix_permissions(0o755);
let file = File::create(&new_archive_path).unwrap();
let mut new_zip = zip::ZipWriter::new(file);
for i in 0..archive.len() {
let mut file_in_archive = archive.by_index(i).unwrap();
let file_in_archive_name = &file_in_archive.name().to_string();
if file_in_archive_name.to_string().ends_with(".xml"){
let mut contents = String::new();
file_in_archive.read_to_string(&mut contents).unwrap();
let mut new_contents = contents.to_string();
for value in &values {
let (target, new_target) = value;
new_contents = new_contents.replace(&target.to_string(), &new_target.to_string());
}
new_zip.start_file(file_in_archive_name, options)?;
new_zip.write_all(new_contents.as_bytes())?;
} else {
new_zip.start_file(file_in_archive_name.to_string(), options)?;
let mut buffer = Vec::new();
file_in_archive.read_to_end(&mut buffer)?;
new_zip.write_all(&buffer)?;
}
}
std::fs::rename(new_archive_path, docx_path).unwrap();
Ok(())
}
pub fn docx_replace_values_save(
docx_path: PathBuf,
values: Vec<(String, String)>,
new_docx_path: PathBuf,
) -> Result<(), Box<dyn std::error::Error>> {
use std::fs::File;
use std::io::prelude::*;
use zip::read::ZipArchive;
use zip::write::FileOptions;
use zip::CompressionMethod;
let file = File::open(docx_path.clone())?;
let mut archive = ZipArchive::new(file)?;
let new_archive_path = "new_archive.zip";
let options = FileOptions::default()
.compression_method(CompressionMethod::Stored)
.unix_permissions(0o755);
let file = File::create(&new_archive_path).unwrap();
let mut new_zip = zip::ZipWriter::new(file);
for i in 0..archive.len() {
let mut file_in_archive = archive.by_index(i).unwrap();
let file_in_archive_name = &file_in_archive.name();
if file_in_archive_name.to_string() == "word/document.xml" {
let mut contents = String::new();
file_in_archive.read_to_string(&mut contents).unwrap();
let mut new_contents = contents.to_string();
for value in &values {
let (target, new_target) = value;
new_contents = new_contents.replace(target, &new_target);
}
new_zip.start_file("word/document.xml", options)?;
new_zip.write_all(new_contents.as_bytes())?;
} else {
new_zip.start_file(file_in_archive_name.to_string(), options)?;
let mut buffer = Vec::new();
file_in_archive.read_to_end(&mut buffer)?;
new_zip.write_all(&buffer)?;
}
}
std::fs::rename(new_archive_path, new_docx_path).unwrap();
Ok(())
}
pub fn docx_get_xml(docx_path: impl AsRef<str>) -> Result<String, Box<dyn std::error::Error>> {
let reader = File::open(docx_path.as_ref())?;
let mut zip = ZipArchive::new(reader)?;
let mut file = zip.by_name("word/document.xml")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
#[cfg(target_os = "unix")]
pub fn docx_remove_read_only(
docx_path: impl AsRef<str>,
) -> Result<(), Box<dyn std::error::Error>> {
use std::fs::{metadata, set_permissions};
use std::os::unix::fs::PermissionsExt;
let docx_path = docx_path.as_ref();
let meta = metadata(docx_path)?;
let mut permissions = meta.permissions();
let mode = permissions.mode();
let new_mode = mode & !0o400;
permissions.set_mode(new_mode);
set_permissions(docx_path, permissions)?;
Ok(())
}
pub fn parse_xml_get_content(xml: impl AsRef<str>) -> Vec<String> {
use xml::reader::{EventReader, XmlEvent};
let parser = EventReader::from_str(xml.as_ref());
let mut content = vec![];
for e in parser {
match e {
#[allow(warnings)]
Ok(XmlEvent::StartElement { name, .. }) => {}
#[allow(warnings)]
Ok(XmlEvent::EndElement { name }) => {}
Ok(XmlEvent::Characters(chars)) => {
content.push(chars);
}
Err(e) => {
println!("Error: {}", e);
break;
}
_ => {}
}
}
content
}
pub fn docx_get_content(
docx_path: impl AsRef<str>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let xml = crate::docx::docx_get_xml(docx_path)?;
Ok(crate::docx::parse_xml_get_content(xml))
}
}
#[cfg(feature = "docx")]
pub use docx::*;