RunUtil.java
package pro.verron.officestamper.core;
import jakarta.xml.bind.JAXBElement;
import org.docx4j.model.styles.StyleUtil;
import org.docx4j.wml.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import pro.verron.officestamper.api.OfficeStamperException;
import java.util.Objects;
import static java.util.stream.Collectors.joining;
import static pro.verron.officestamper.utils.WmlFactory.*;
/**
* Utility class to handle runs.
*
* @author Joseph Verron
* @author Tom Hombergs
* @version ${version}
* @since 1.0.0
*/
public class RunUtil {
private static final String PRESERVE = "preserve";
private static final Logger log = LoggerFactory.getLogger(RunUtil.class);
private RunUtil() {
throw new OfficeStamperException("Utility class shouldn't be instantiated");
}
/**
* Returns the text string of a run.
*
* @param run the run whose text to get.
*
* @return {@link String} representation of the run.
*/
public static String getText(R run) {
return run.getContent()
.stream()
.map(RunUtil::getText)
.collect(joining());
}
/**
* Returns the textual representation of a run child
*
* @param content the run child to represent textually
*
* @return {@link String} representation of run child
*/
public static CharSequence getText(Object content) {
return switch (content) {
case JAXBElement<?> jaxbElement -> getText(jaxbElement.getValue());
case Text text -> getText(text);
case R.Tab ignored -> "\t";
case R.Cr ignored -> "\n";
case Br br when br.getType() == null -> "\n";
case Br br when br.getType() == STBrType.TEXT_WRAPPING -> "\n";
case Br br when br.getType() == STBrType.PAGE -> "\n";
case Br br when br.getType() == STBrType.COLUMN -> "\n";
case R.NoBreakHyphen ignored -> "‑";
case R.SoftHyphen ignored -> "\u00AD";
case R.LastRenderedPageBreak ignored -> "";
case R.AnnotationRef ignored -> "";
case R.CommentReference ignored -> "";
case Drawing ignored -> "";
case R.Sym sym -> "<sym(%s, %s)>".formatted(sym.getFont(), sym.getChar());
default -> {
log.debug("Unhandled object type: {}", content.getClass());
yield "";
}
};
}
private static CharSequence getText(Text text) {
String value = text.getValue();
String space = text.getSpace();
return Objects.equals(space, PRESERVE)
? value // keeps spaces if spaces are to be preserved (LibreOffice seems to ignore the "space" property)
: value.trim(); // trimming value if spaces are not to be preserved (simulates behavior of Word;)
}
/**
* Creates a new run with the specified text and inherits the style of the parent paragraph.
*
* @param text the initial text of the run.
*
* @return the newly created run.
*/
public static R create(String text, PPr paragraphPr) {
R run = newRun(text);
applyParagraphStyle(run, paragraphPr);
return run;
}
/**
* Applies the style of the given paragraph to the given content object (if the content object is a Run).
*
* @param run the Run to which the style should be applied.
*/
public static void applyParagraphStyle(R run, @Nullable PPr paragraphPr) {
if (paragraphPr == null) return;
var runPr = paragraphPr.getRPr();
if (runPr == null) return;
RPr runProperties = new RPr();
StyleUtil.apply(runPr, runProperties);
run.setRPr(runProperties);
}
/**
* Sets the text of the given run to the given value.
*
* @param run the run whose text to change.
* @param text the text to set.
*/
public static void setText(R run, String text) {
run.getContent()
.clear();
Text textObj = newText(text);
run.getContent()
.add(textObj);
}
static int getLength(R run) {
return getText(run)
.length();
}
static String getSubstring(R run, int beginIndex) {
return getText(run)
.substring(beginIndex);
}
static String getSubstring(R run, int beginIndex, int endIndex) {
return getText(run)
.substring(beginIndex, endIndex);
}
static R create(String text, RPr rPr) {
R newStartRun = newRun(text);
newStartRun.setRPr(rPr);
return newStartRun;
}
}