use crate::namespaces::{O, VML, WP};
use crate::xmllinq::{Dom, NodeId, XName};
fn id_attr() -> XName {
XName::get("id", "")
}
pub fn fix_up_doc_pr_ids(dom: &mut Dom, root: NodeId) {
let id = id_attr();
let nodes: Vec<NodeId> = dom
.descendants(root, None)
.into_iter()
.filter(|&e| {
dom.name(e).is_some_and(|n| n.local_name() == "docPr")
|| dom.name(e).as_ref() == Some(&WP::name("docPr"))
})
.collect();
let mut seen = std::collections::HashSet::new();
let nodes: Vec<NodeId> = nodes.into_iter().filter(|n| seen.insert(*n)).collect();
for (next, e) in (1u32..).zip(nodes) {
dom.set_attribute_value(e, &id, Some(&next.to_string()));
}
}
pub fn fix_up_shape_ids(dom: &mut Dom, root: NodeId) {
let id = id_attr();
let shape_id = XName::get("ShapeID", "");
for (next, shape) in (1u32..).zip(dom.descendants(root, Some(&VML::name("shape")))) {
let old = dom.attribute(shape, &id).map(|s| s.to_string());
let new = next.to_string();
dom.set_attribute_value(shape, &id, Some(&new));
if let (Some(old), Some(parent)) = (old, dom.parent(shape)) {
for ole in dom.elements(parent, Some(&O::name("OLEObject"))) {
if dom.attribute(ole, &shape_id).map(|s| s.to_string()) == Some(old.clone()) {
dom.set_attribute_value(ole, &shape_id, Some(&new));
}
}
}
}
}
pub fn fix_up_shape_type_ids(dom: &mut Dom, root: NodeId) {
let id = id_attr();
let type_attr = XName::get("type", "");
for (next, st) in (1u32..).zip(dom.descendants(root, Some(&VML::name("shapetype")))) {
let old = dom.attribute(st, &id).map(|s| s.to_string());
let new = next.to_string();
dom.set_attribute_value(st, &id, Some(&new));
if let (Some(old), Some(parent)) = (old, dom.parent(st)) {
let want = format!("#{old}");
for shape in dom.elements(parent, Some(&VML::name("shape"))) {
if let Some(t) = dom.attribute(shape, &type_attr).map(|s| s.to_string()) {
if t == old {
dom.set_attribute_value(shape, &type_attr, Some(&new));
} else if t == want {
dom.set_attribute_value(shape, &type_attr, Some(&format!("#{new}")));
}
}
}
}
}
}
pub fn fix_up_group_ids(dom: &mut Dom, root: NodeId) {
let id = id_attr();
for (next, g) in (1u32..).zip(dom.descendants(root, Some(&VML::name("group")))) {
dom.set_attribute_value(g, &id, Some(&next.to_string()));
}
}
pub fn fix_up_drawing_ids_in_package(docx: &[u8]) -> Result<Vec<u8>, crate::opc::OpcError> {
use crate::opc::PartFs;
let mut pkg = PartFs::open(docx)?;
let main = pkg
.main_document_part()
.unwrap_or_else(|| "word/document.xml".to_string());
let Some(xml) = pkg.part_string(&main) else {
return Err(crate::opc::OpcError::PartNotFound(format!(
"main document missing: {main}"
)));
};
let mut dom = Dom::new();
let doc = dom.parse_xdocument(&xml);
let Some(root) = dom.root(doc) else {
return Err(crate::opc::OpcError::PartNotFound(format!(
"main document has no root element: {main}"
)));
};
fix_up_doc_pr_ids(&mut dom, root);
fix_up_shape_ids(&mut dom, root);
fix_up_shape_type_ids(&mut dom, root);
pkg.set_part(&main, dom.serialize_element(root).into_bytes());
pkg.to_zip()
}