Skip to content

Softeq/docx-placeholders

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

docx-placeholders

Introducation

The Docx-placeholders is a library which allows defining and filling placeholders in .docx template files. There are many cases when business requires a template to be filled with values from java. The approach allows usual .docx files using as templates where placeholders can be added and programmatically evaluated/filled.

A template .docx file containing "Hello {firstName} {lastName}" after evaluation converted to "Hello John Smith".

Documentation

Content:

  1. Quick Start
  2. Supported tags
  3. Customize tag start and end tokens
  4. Samples

Quick Start

The simplest start is to define a template .docx document with placeholders like ${{firstName}} and ${{lastName}} and then evaluate (fill) the template with the following code.

//read template .docx
InputStream templateStream = getClass().getResourceAsStream("/some/resource/MyTemplate.docx");
//create a map with placeholders - key is placeholder name and value is appropriate value
Map<String, String> placeholdersValuesMap = new HashMap<String, String>() {{
    put("firstName", "John");
    put("lastName", "Smith");
}};
//Create context and set just one map tag processor
DocxTemplateFillerContext context = new DocxTemplateFillerContext();
context.setProcessors(Collections.singletonList(new MapTagProcessor(placeholdersValuesMap)));
//create target stream to store the filled template
ByteArrayOutputStream filledTemplateStream = new ByteArrayOutputStream();

DocxTemplateFiller filler = new DocxTemplateFiller();
//fill the template with the defined placeholders
filler.fillTemplate(templateStream, filledTemplateStream, context); 

Supported tags

Map Tag Processor

The tag processor is based on a Map<String, String>. Key of the Map is a tag name and value contains text to be used when the placeholder is filled. The code example is described in the Quick Start section. Developer defines a Map with tags to be filled and the values are placed in the template.

The simplest case described above is not enough in many cases. Let's consider a few alternative cases.

POJO fields and collections

When there is a POJO (Plain Old Java Object) java object and we need the fields of the POJO to be used in a template another tag processors should be used - PojoFieldTagProcessor and PojoCollectionTagProcessor.

A template below shows tags "field" and "collection".

Alt text

The POJO could be represented as the following JSON.

{
  "companyName": "TestCompany",
  "projects": [
    {
      "projectName": "Project One"
    },
    {
      "projectName": "Project Two"
    }
  ]
}

Here ${{collection:projects}} means reference to the POJO field named "projects".

To evaluate the template and fill declared placeholders tag the following code to be executed:

//read template .docx
InputStream templateStream = getClass().getResourceAsStream("/some/resource/MyTemplate.docx");
//create context and add two tag processors to handle the tags
DocxTemplateFillerContext context = new DocxTemplateFillerContext();
context.setProcessors(Arrays.asList(new PojoCollectionTagProcessor(), new PojoFieldTagProcessor()));

//place the POJO as a value root in the context
context.push(null, createPOJOexampleFromJSON());

//create target stream to store the filled template
ByteArrayOutputStream filledTemplateStream = new ByteArrayOutputStream();

DocxTemplateFiller filler = new DocxTemplateFiller();
//fill the template with the defined placeholders
filler.fillTemplate(templateStream, filledTemplateStream, context); 

After evaluation the filled .docx template is following: Alt text

How it works?

Each tag is evaluated by own Tag Processor. List of tag processors are defined in the DocxTemplateFillerContext. For each detected tag the list is iterated to find the processor which can process the tag. In the described case one processor can process field tags and another one processes collection. The field processing tag gets tag value from the POJO object placed as the DocxTemplateFillerContext root value.

When the collection tag is met the collection processor does the following:

  1. Detects tag body - all the body elements - paragraphs and tables between open and close tag.
  2. Found the tag referenced collection in the DocxTemplateFillerContext value root.
  3. Starts iterating the collection
  4. Each collection item is placed to be the new value root.
  5. The tag body (filled in the step 1) is cloned including all the nested elements and theri styles, evaluated (tags filled) with the new local value root. (So the ${{field:projectName}} finds value in the local root - collection member).
  6. The evaluated tag body for the single collection item is inserted.
  7. After all the collection items are processed and all the evaluated copies of tag body are inserted the original elements are removed (including the tag start and end).

The same approach is applied to the nested tags (e.g. collection in a collection). Company has a projects collection and each project has a list of developers. Value root is pushed in a stack defined in the DocxTemplateFillerContext and restored after tag body evaluation.

NOTE: The DocxTemplateFillerContext works with a list of TagProcessors. For each found tag the list is iterated asking each TagProcessor whether it can process the found tag. The method of TagProcessor interface.

boolean canProcessTag(TagInfo tag);

If a processor returns yes filler passes the control to the processor to fill the tag.

POJO nested block

Suppose there is a POJO User with nested POJO Address

public class UserDto {
    private String firstName;
    private String lastName;
    private AddressDto address;
    //getters and setters
}
public class AddressDto {
    private String country;
    private String city;
    private String street;
    //getters and setters
}

To show values of the nested Address POJO the ${{block:address}} tag. When the "block" tag is met the referenced field becomes the value root and all the inner "field" tags e.g. ${{field:country}} use the fields of new value root - Address.

So for the template

Alt text

And after evaluation the filled template

Alt text

Link and Image tags

There are some cases when just plain text is not enough. The cases when we need a link or image require more than just text. For such cases separate interfaces were defined: For links

public interface TagLinkData {
    String getText();
    String getUrl();
    String getColor();
}

For images

public interface TagImageData {
    String getTitle();
    String getContentType();
    InputStream getSourceStream();
    Integer getWidth();
    Integer getHeight();
}

If a link must be inserted in a template a tag "link" should be defined ${{link:/}} if the link is POJO or ${{link:linkField/}} if the link is a field of the value root POJO. For Images ${{image:/}} and ${{image:imageField/}} In the first case POJO placed in the context root must implement the TagLinkData interface. The methods are used to return link attributes - text or the link, reference URL, and the link color.

Image interface includes more methods to define image size, content etc.

Customize tag start/end

There are different tag open and close tokens. E.g. HTML and XML are supposed to use '<' and '>' chars. The library by default use '${{' and '}}' tokens but there is a way to customize them. The following code allows to set XML like tokens for tag start/end:

DocxTemplateFillerContext context = new DocxTemplateFillerContext();
context.setTagStart("<");
context.setTagEnd(">");

After that tags like "<field:firstName>" can be added to the templates to be evaluated.

Samples

To be defined.

About

Docx-placeholders for using docx as a template and provide flexible filling of placeholders

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages