Skip to content

verronpro/docx-stamper

Repository files navigation

docx-stamper

Build Status Build Status Build Status

docx-stamper is a Java template engine for docx documents. You create a template .docx document with your favorite word processor and feed it to a DocxStamper instance to create a document based on the template at runtime. Example code:

class Example {
    public static void main(String[] args) {
        // your own POJO against which expressions found in the template will be resolved
        var context = new YourPojoContext(_, _ , _);

        // Path to your .docx template file
        var templatePath = Paths.get("your/docx/template/file.docx");
        // Path to write the resulting .docx document
        var outputPath = Paths.get("your/desired/output/path.docx");

        var stamper = new DocxStamper();

        try(
            var template = Files.newInputStream(templatePath);
            var output = Files.newOutputStream(outputPath)
        ) {
            stamper.stamp(template, context, output);
        }
    }
}

Replacing Expressions in a .docx Template

The main feature of docx-stamper is replacement of expressions within the text of the template document. Simply add expressions like ${person.name} or ${person.name.equals("Homer") ? "Duff" : "Budweiser"} in the text of your .docx template and provide a context object against which the expression can be resolved. docx-stamper will try to keep the original formatting of the text in the template intact. You can use the full feature set of Spring Expression Language (SpEL).

The value that an expression resolves to may be of the following types:

Table 1. Type that can be resolved to an element in the .docx document

Type of expression value

Effect

java.lang.Object

The expression is replaced by the String representation of the object (String.valueOf()).

java.lang.String

The expression is replaced with the String value.

java.util.Date

The expression is replaced by a formatted Date string (by default "dd.MM.yyyy"). You can change the format string by registering your own DateResolver.

java.time.LocalDate

The expression is replaced by a formatted Date string (by default DateTimeFormatter.ISO_LOCAL_DATE). You can change the format string by registering your own.

java.time.LocalDateTime

The expression is replaced by a formatted Date string (by default DateTimeFormatter.ISO_LOCAL_DATE_TIME). You can change the format string by registering your own.

java.time.LocalTime

The expression is replaced by a formatted Date string (by default DateTimeFormatter.ISO_LOCAL_TIME). You can change the format string by registering your own.

org.wickedsource…​Image

The expression is replaced with an inline image.

If an expression cannot be resolved successfully, it will be skipped (meaning the expression stays in the document as it was in the template). To support more than the above types, you can implement your own ObjectResolver. To register your it with docx-stamper, use the following code:

class Main {
    public static void main(String... args) {
        // instance of your own ObjectResolver implementation
        var resolver = new StringResolver(YourCustomType.class){
            @Override public String resolve(YourCustomType object){
                return custom.yourCustomProperty(); // or any convoluted method
            }
        };

        var configuration = new DocxStamperConfiguration();
        configuration.addResolver(resolver);
        var stamper = new DocxStamper<>(configuration);
    }
}

Customizing the SpEL Evaluation Context

If you want to take more control over the evaluation of expressions, you can implement a EvaluationContextConfigurer and customize Springs StandardEvaluationContext to your needs. You can register an EvaluationContextConfigurer like this:

class Main {
    public static void main(String... args) {
        var evalContextConfigurer = new NoOpEvaluationContextConfigurer();
        var configuration = new DocxStamperConfiguration()
            .setEvaluationContextConfigurer(configurer);
        var stamper = new DocxStamper<>(configuration);
    }
}

Adding custom functions to the Expression Language

If you want to create custom functions (for different number formats or different date formats, for example), you can register functions which can then be used in the expression language. The following code, for example, adds a function toUppercase(String) which can be used within the .docx document to uppercase a String:

class Main {
    public static void main(String... args) {
        interface UppercaseFunction {
            String toUppercase(String string);
        }

        var configuration = new DocxStamperConfiguration()
            .exposeInterfaceToExpressionLanguage(UppercaseFunction.class, String::toUppercase);
        var stamper = new DocxStamper<>(configuration);
    }
}

Comment Processors

Besides replacing expressions, docx-stamper can process comments on paragraphs of text in the template .docx document and do manipulations on the template based on these comments. By default, you can use the following expressions in comments:

Table 2. Default activated comment processors
Expression in .docx comment Effect

displayParagraphIf(boolean)

The commented paragraph is only displayed in the resulting .docx document if the boolean condition resolves to true.

displayTableRowIf(boolean)

The table row surrounding the commented paragraph is only displayed in the resulting .docx document if the boolean condition resolves to true.

displayTableIf(boolean)

The whole table surrounding the commented paragraph is only displayed in the resulting .docx document if the boolean condition resolves to true.

repeatTableRow(List<Object>)

The table row surrounding the commented paragraph is copied once for each object in the passed-in list. Expressions found in the cells of the table row are evaluated against the object from the list.

repeatDocPart(List<Object>)

Repeat the part of the document surrounded by the comment. The document part is copied once for each object in the passed-in list. Expressions found in the elements of the document part are evaluated against the object from the list. Can be used instead of repeatTableRow and repeatParagraph if you want to repeat more than table rows and paragraphs.

replaceWordWith(expression)

Replaces the commented word (must be a single word) with the value of the given expression.

resolveTable(StampTable)

Replace a table (that must have one column and two rows) with the values given by the StampTable. The StampTable contains a list of headers for columns, and a 2-level list of rows containing values for each column.

If a comment cannot be processed, by default an exception will be thrown. Successfully processed comments are removed from the document. You can add support to more expressions in comments by implementing your own ICommentProcessor. To register your comment processor to docx-stamper, use the following code:

class Main {
    public static void main(String... args) {
        // interface defining the methods to expose to the expression language
        interface IYourCommentProcessor {
            void yourComment(String _); // 1+ argument of the type you expect to see in the document
            void yourSecondComment(String _, CustomType _); // theoretically, any number of comment can be added
        }
        class YourCommentProcessor extends BaseCommentProcessor {
            @Override public void commitChanges(WordprocessingMLPackage document) {/*Do something to the document*/}
            @Override public void reset() {/* reset processor state for re-run of the stamper */}
        }
        var commentProcessor = new YourCommentProcessor();
        var configuration = new DocxStamperConfiguration()
                .addCommentProcessor(IYourCommentProcessor.class, commentProcessor);
        var stamper = new DocxStamper<>(configuration);

    }
}

For an in-depth description of how to create a comment processor, see the javadoc of ICommentProcessor.

Conditional Display and Repeating of Elements in Headers or Footers

The docx file format does not allow comments in Headers or Footers of a document. To be able to conditionally display content in a header or footer, surround the expression you would put in a comment with "#{}" and put it at the beginning of the paragraph you want to manipulate. The expression will be evaluated as it would be in a comment.

Error Handling

By default, DocxStamper fails with an UnresolvedExpressionException if an expression within the document or within the comments cannot be resolved successfully. If you want to change this behavior, you can do the following:

class Main {
    public static void main(String... args) {
        var configuration = new DocxStamperConfiguration()
                .setFailOnUnresolvedExpression(false);
        var stamper = new DocxStamper<>(configuration);
    }
}

Sample Code

The source code contains a set of tests show how to use the features. If you want to run them yourself, clone the repository and run mvn test with the system property -DkeepOutputFile=true so that the resulting .docx documents will not be cleaned up and let you view them. The resulting files will be stored in your local temp folder. Watch the logging output for the exact location of the files).

If you want to have a look at the .docx templates used in the tests, have a look at the sources subfolder in the test folder.

Maven coordinates

To include docx-stamper in your project, you can use the following maven coordinates in your dependency management system: go to last documented version

Note that as of version 1.4.0, you have to provide the dependency to your version of Docx4J yourself:

<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j</artifactId>
    <version>6.1.2</version>
</dependency>

This way, you can choose which version of Docx4J you want to use instead of having it dictated by docx-stamper.

Contribute

If you have an issue or created a comment processor or type resolver that you think deserves to be part of the default distribution, feel free to open an issue or - even better - a pull request with your contribution.