Code: Select all
import org.odftoolkit.odfdom.doc.OdfTextDocument;
import org.odftoolkit.odfdom.dom.OdfContentDom;
import org.odftoolkit.odfdom.dom.OdfStylesDom;
import org.odftoolkit.odfdom.dom.element.style.*;
import org.odftoolkit.odfdom.dom.element.text.*;
import org.odftoolkit.odfdom.incubator.doc.style.OdfStyle;
import org.w3c.dom.*;
import javax.xml.xpath.*;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
public class StyleNamePrepender {
public static void prependStyleNames(String inputPath, String outputPath) throws Exception {
// Extract filename without extension
String filename = new File(inputPath).getName();
filename = filename.substring(0, filename.lastIndexOf('.'));
String prefix = filename + "_";
// Load the ODT document
OdfTextDocument odt = OdfTextDocument.loadDocument(inputPath);
// Map to store old style names to new style names
Map<String, String> styleNameMap = new HashMap<>();
// Process styles in styles.xml
OdfStylesDom stylesDom = odt.getStylesDom();
processStylesInDom(stylesDom, prefix, styleNameMap);
// Process styles in content.xml
OdfContentDom contentDom = odt.getContentDom();
processStylesInDom(contentDom, prefix, styleNameMap);
// Update all style references throughout the document
updateStyleReferences(odt, styleNameMap);
// Save the modified document
odt.save(outputPath);
odt.close();
System.out.println("Style names prepended successfully!");
System.out.println("Prefix used: " + prefix);
System.out.println("Total styles renamed: " + styleNameMap.size());
}
private static void processStylesInDom(Node dom, String prefix, Map<String, String> styleNameMap) {
// Process using direct DOM traversal instead of XPath
processNodeRecursively(dom, prefix, styleNameMap);
}
private static void processNodeRecursively(Node node, String prefix, Map<String, String> styleNameMap) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
String localName = element.getLocalName();
// Check if this is a style element
boolean isStyleElement = false;
String nameAttrName = null;
if ("style".equals(localName) || "default-style".equals(localName) ||
"page-layout".equals(localName)) {
isStyleElement = true;
nameAttrName = "style:name";
} else if ("list-style".equals(localName)) {
isStyleElement = true;
nameAttrName = "style:name";
} else if ("number-style".equals(localName) || "date-style".equals(localName) ||
"time-style".equals(localName) || "currency-style".equals(localName) ||
"percentage-style".equals(localName)) {
isStyleElement = true;
nameAttrName = "style:name";
} else if ("master-page".equals(localName)) {
isStyleElement = true;
nameAttrName = "style:name";
}
if (isStyleElement) {
// Try to get the name attribute
String oldName = element.getAttributeNS(
"urn:oasis:names:tc:opendocument:xmlns:style:1.0", "name");
if (oldName != null && !oldName.isEmpty()) {
String newName = prefix + oldName;
// Update the attribute
element.setAttributeNS(
"urn:oasis:names:tc:opendocument:xmlns:style:1.0",
"style:name",
newName);
// Store mapping
styleNameMap.put(oldName, newName);
System.out.println("Renamed style: " + oldName + " -> " + newName);
}
}
}
// Process child nodes
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
processNodeRecursively(children.item(i), prefix, styleNameMap);
}
}
private static void updateStyleReferences(OdfTextDocument odt, Map<String, String> styleNameMap) {
// Update references in both content and styles DOMs
Document[] doms = {odt.getContentDom(), odt.getStylesDom()};
for (Document dom : doms) {
updateReferencesRecursively(dom, styleNameMap);
}
}
private static void updateReferencesRecursively(Node node, Map<String, String> styleNameMap) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
// Comprehensive list of attributes that reference style names
String[] refAttrs = {
"style-name",
"parent-style-name",
"list-style-name",
"data-style-name",
"page-layout-name",
"master-page-name",
"next-style-name",
"default-cell-style-name",
"text-style-name", // for draw elements
"default-style-name", // for tables
"apply-style-name", // for conditional formatting
"page-style-name", // for page breaks
"tab-stop-style-name", // for tab stops
"drop-cap-style-name", // for drop caps
"num-format", // can reference styles
"cell-style-name", // for table cells
"paragraph-style-name", // explicit paragraph styles
"ruby-style-name", // for ruby text
"leader-style-name", // for leader lines
"default-outline-level-style" // for outline numbering
};
// All ODF namespaces that might contain style references
String[] namespaces = {
"urn:oasis:names:tc:opendocument:xmlns:text:1.0", // text:
"urn:oasis:names:tc:opendocument:xmlns:style:1.0", // style:
"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0", // draw:
"urn:oasis:names:tc:opendocument:xmlns:table:1.0", // table:
"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0", // presentation:
"urn:oasis:names:tc:opendocument:xmlns:fo:1.0", // fo:
"urn:oasis:names:tc:opendocument:xmlns:number:1.0" // number:
};
for (String attrName : refAttrs) {
for (String ns : namespaces) {
String oldStyleName = element.getAttributeNS(ns, attrName);
if (oldStyleName != null && !oldStyleName.isEmpty()) {
String newStyleName = styleNameMap.get(oldStyleName);
if (newStyleName != null) {
String nsPrefix = getNamespacePrefix(ns);
element.setAttributeNS(ns, nsPrefix + ":" + attrName, newStyleName);
System.out.println("Updated reference: " + oldStyleName + " -> " + newStyleName +
" (in " + element.getLocalName() + ", attr: " + attrName + ")");
}
}
}
}
// Also check for attributes without namespace (some older ODT files)
NamedNodeMap attrs = element.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Node attr = attrs.item(i);
String attrName = attr.getNodeName();
// Check if attribute name contains "style" and could be a reference
if (attrName.contains("style") || attrName.contains("Style")) {
String oldStyleName = attr.getNodeValue();
String newStyleName = styleNameMap.get(oldStyleName);
if (newStyleName != null) {
attr.setNodeValue(newStyleName);
System.out.println("Updated non-namespaced reference: " + oldStyleName +
" -> " + newStyleName + " (attr: " + attrName + ")");
}
}
}
}
// Process child nodes
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
updateReferencesRecursively(children.item(i), styleNameMap);
}
}
private static String getNamespacePrefix(String namespace) {
switch (namespace) {
case "urn:oasis:names:tc:opendocument:xmlns:text:1.0":
return "text";
case "urn:oasis:names:tc:opendocument:xmlns:style:1.0":
return "style";
case "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0":
return "draw";
case "urn:oasis:names:tc:opendocument:xmlns:table:1.0":
return "table";
case "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0":
return "presentation";
case "urn:oasis:names:tc:opendocument:xmlns:fo:1.0":
return "fo";
case "urn:oasis:names:tc:opendocument:xmlns:number:1.0":
return "number";
default:
return "style";
}
}
public static void main(String[] args) {
try {
if (args.length < 2) {
System.out.println("Usage: java StyleNamePrepender <input.odt> <output.odt>");
return;
}
String inputPath = args[0];
String outputPath = args[1];
prependStyleNames(inputPath, outputPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}