Preprocessors.java
package pro.verron.officestamper.preset;
import org.docx4j.TraversalUtil;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.utils.TraversalUtilVisitor;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.ProofErr;
import org.docx4j.wml.R;
import org.docx4j.wml.RPr;
import pro.verron.officestamper.api.OfficeStamperException;
import pro.verron.officestamper.api.PreProcessor;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
/**
* A helper class that provides pre-processing functionality for WordprocessingMLPackage documents.
*/
public class Preprocessors {
private Preprocessors() {
throw new OfficeStamperException("Preprocessors cannot be instantiated");
}
/**
* Returns a PreProcessor object that merges same style runs that are next to each other in a
* WordprocessingMLPackage document.
*
* @return a PreProcessor object that merges similar runs.
*/
public static PreProcessor mergeSimilarRuns() {
return new MergeSameStyleRuns();
}
/**
* Returns a PreProcessor object that removes all {@link ProofErr} elements from the WordprocessingMLPackage
* document.
*
* @return a PreProcessor object that removes ProofErr elements.
*/
public static PreProcessor removeLanguageProof() {
return new RemoveProofErrors();
}
private static class MergeSameStyleRuns
implements PreProcessor {
/**
* {@inheritDoc}
*/
@Override
public void process(WordprocessingMLPackage document) {
var mainDocumentPart = document.getMainDocumentPart();
var visitor = new SimilarRunVisitor();
TraversalUtil.visit(mainDocumentPart, visitor);
for (List<R> similarStyleRuns : visitor.getSimilarStyleRuns()) {
R firstRun = similarStyleRuns.get(0);
var runContent = firstRun.getContent();
var firstRunContent = new LinkedHashSet<>(runContent);
var firstRunParentContent = ((ContentAccessor) firstRun.getParent()).getContent();
for (R r : similarStyleRuns.subList(1, similarStyleRuns.size())) {
firstRunParentContent.remove(r);
firstRunContent.addAll(r.getContent());
}
runContent.clear();
runContent.addAll(firstRunContent);
}
}
private static class SimilarRunVisitor
extends TraversalUtilVisitor<R> {
private final List<List<R>> similarStyleRuns = new ArrayList<>();
public List<List<R>> getSimilarStyleRuns() {
return similarStyleRuns;
}
@Override
public void apply(R element, Object parent, List<Object> siblings) {
RPr rPr = element.getRPr();
int currentIndex = siblings.indexOf(element);
List<R> similarStyleConcurrentRun = siblings
.stream()
.skip(currentIndex)
.takeWhile(o -> o instanceof R run && Objects.equals(run.getRPr(), rPr))
.map(R.class::cast)
.toList();
if (similarStyleConcurrentRun.size() > 1)
similarStyleRuns.add(similarStyleConcurrentRun);
}
}
}
private static class RemoveProofErrors
implements PreProcessor {
/**
* {@inheritDoc}
*/
@Override
public void process(WordprocessingMLPackage document) {
var mainDocumentPart = document.getMainDocumentPart();
var visitor = new ProofErrVisitor();
TraversalUtil.visit(mainDocumentPart, visitor);
for (ProofErr proofErr : visitor.getProofErrs()) {
var proofErrParent = proofErr.getParent();
if (proofErrParent instanceof ContentAccessor parent) {
var parentContent = parent.getContent();
parentContent.remove(proofErr);
}
}
}
private static class ProofErrVisitor
extends TraversalUtilVisitor<ProofErr> {
private final List<ProofErr> proofErrs = new ArrayList<>();
@Override
public void apply(ProofErr element, Object parent1, List<Object> siblings) {
proofErrs.add(element);
}
public List<ProofErr> getProofErrs() {
return proofErrs;
}
}
}
}