Today there exists several RTF java parser like iText, ecs, Jakarta POI - HWPF or RTFEditorKit (parser RTF used by Swing). With iText and ecs, you can't read RTF from file. The last news of Jakarta POI - HWPF is 2003. Where is her status today? RTFEditorKit is RTF parser very completed, but among RTF version, there are problems for reading RTF.
When I searched java RTF parser, I wanted that parser was able to :
That's why I decided to create RTFTemplate, a RTF simple parser which can read RTF from file and update any RTF content. When you say "merging data with template" in JAVA, you think Velocity. Morever Velocity exist too for C#, this template engine is called NVelocity. So Velocity engine was the best solution to merge data with RTF template.
While I searched RTF Parser, I found on mail archive of velocity RTFTemplateEngine idea and source. RTFTemplate based on founded source.
However create (at hand) velociy template are 2 problems :
To resolve these problems, I create simply RTF parser which is able to transform MERGEFIELD and BOOKMARK of the RTF model into velocity macro.
When you use RTFTemplate 2 steps are necessary :
Here is diagram which describes basic RTFTemplate process :
When your RTF model must iterate on JAVA objects list, #foreach velocity macro are generated (on step 1). To generate this macro velocity, RTFTemplate use MERGEFIELD type. If MERGEFIELD is list type, RTFTemplate generate #foreach. To know if MERGEFIELD is list or not, RTFTemplate use context. There are two methods :
RTFTemplate template = engine.createTemplate(new File("usecases/models/jakartavelocityproject/jakarta-velocity-model.rtf")); template.put("header_developer_name", "Name"); ...
// Load XML Fields Available InputStream xmlFieldsAvailable = new FileInputStream(new File("usecases/models/jakartavelocityproject/jakarta-velocity.fields.xml")); // Initialize RTFTemplate with XML fields available RTFTemplate template = engine.createTemplate(new File("usecases/models/jakartavelocityproject/jakarta-velocity-model.rtf"), xmlFieldsAvailable); ...
At end of Step 1, RTF & Velocity macro is generated. But what is this RTF content? Is it file? is it Reader? With RTFTemplate you can manage this RTF content. There are two strategies :
In this strategy, RTF content (& Velocity macro) is File or content stored into database. This step is executed the first time when RTF content (& Velocity macro) doesn't exist. The second time this RTF content is not again generate. This strategy is performant but, developer must manage this RTF content. For use this strategy you use RTFTemplate and RTFVelocityTemplate classes :
// STEP 1 : generate RTF File with Velocity Macro RTFTemplate template = engine.createTemplate(new File("usecases/models/jakartavelocityproject/jakarta-velocity-model.rtf")); ... String rtfVelocity = outDirectory + "/jakarta-velocity-model.rtf.velocity.rtf"; template.saveRTFVelocity(rtfVelocity); // STEP 2 : generate RTF File target (by using RTF file with Velocity Macro) RTFVelocityTemplate velocityTemplate = velocityEngine.createTemplate(new File(rtfVelocity)); velocityTemplate.put("header_developer_name", "Name"); ... velocityTemplate.merge(outDirectory + "/jakarta-velocity-model.rtf.rtf");
In this strategy, RTF content (& Velocity macro) is Reader (Stream). This strategy generate each time the RTF content with velocity macro but developer must not manage this RTF content. For use this strategy you use only RTFTemplate class :
RTFTemplate template = engine.createTemplate(new File("usecases/models/jakartavelocityproject/jakarta-velocity-model.rtf")); template.put("header_developer_name", "Name"); ... template.merge(outDirectory + "/jakarta-velocity-model.rtf.rtf");
This step consists to parse RTF model and add velocity macro. For this :
RTF Parser launches events on specials RTF characters or keywords. RTF parser are abstract class. For using parser, you must implements RTF Handler which extends RTF parser. RTFTemplate project use two parsers :
Events of RTF Parser are abstract methods which must be implement by RTF Handler.
RTF Handler extends RTF parser to implement each events of RTF Parser. RTFTemplate project implements two RTF Handler :
Here example of indent RTF code for field :
source RTF (before indentation) :
{\field{\*\fldinst {\lang1036\langfe2057\langnp1036\insrsid331776 MERGEFIELD MY_FIELD \\* MERGEFORMAT }} {\fldrslt {\lang1024\langfe1024\noproof\langnp1036\insrsid331776 \'abMY_FIELD\'bb }} }
target RTF (after indentation) :
{\field {\*\fldinst {\lang1036\langfe2057\langnp1036\insrsid331776 MERGEFIELD MY_FIELD \\* MERGEFORMAT } } {\fldrslt {\lang1024\langfe1024\noproof\langnp1036\insrsid331776 \'abMY_FIELD\'bb } } }
RTFDocumentHandler construct RTFDocument of RTF source model. The RTFDocument is used then for adding velocity macro with RTF Document Transformer.
RTFdocument is the whole RTF content of RTF source model. RTFDocument extends of RTFElement. RTFElement contains list of StringBuffer and other RTFElement. The StringBuffer contains RTF code. It exists several RTFElement type :
{\field {\*\fldinst {\lang1036\langfe2057\langnp1036\insrsid331776 MERGEFIELD MY_FIELD \\* MERGEFORMAT } } {\fldrslt {\lang1024\langfe1024\noproof\langnp1036\insrsid331776 \'abMY_FIELD\'bb } } }
{\*\bkmkstart MY_BOOKMARK }{\*\bkmkend MY_BOOKMARK }
\trowd \irow0\irowband0\lastrow \ts15\trgaph70 ..... {\field {\*\fldinst {\lang1036\langfe2057\langnp1036\insrsid331776 MERGEFIELD MY_FIELD \\* MERGEFORMAT } } ..... } ..... \cellx9086\row
For this example RTFRow is composed by :
For this part, you must know velocity syntax ($object.Value syntax and #foreach, #end velocity macro), see RTF Velocity engine or Home Velocity.
RTFDocumentTransformer is used to transform RTFDocument into another RTFDocument. It must implement the method transform(RTFDocument document) of the interface IRTFTransformer :
public interface IRTFTransformer { public RTFDocument transform(RTFDocument document) throws IOException; }
document parameter is the soure RTF document. This method must return RTFDocument, RTF document transformed.
RTFVelocityTransformer class implements interface IRTFTransformer to manage velocity macro :
public class RTFVelocityTransformer implements IRTFTransformer { .... public RTFVelocityTransformer(VelocityContext context) { .... } .... public RTFDocument transform(RTFDocument document) throws IOException { .... // return RTFDocument with velocity macro. } .... }
This class needs VelocityContext. This will explain in ContextFieldsLoader section.
To explains correctly the usage of velocity macro, we use an example. In this example, we suppose whe have into velocity context :
The two syntax velocity managed by this transformer are :
{\field {\*\fldinst {\lang1036\langfe2057\langnp1036\insrsid331776 MERGEFIELD $customer.Name \\* MERGEFORMAT } } {\fldrslt {\lang1024\langfe1024\noproof\langnp1036\insrsid331776 \'ab$customer.Name\'bb } } }
after transformation, the RTF document transformed contains this RTF code :
{\fldrslt {\lang1024\langfe1024\noproof\langnp1036\insrsid331776 $customer.Name } }
this RTF document will allow to display the name of the customer (after merging template and context with velocity) and MERGEFIELD $customer.Name will not exist more.
@TODO
* Bookmark START_LOOP_1 * Mergefield list <$customers.Name> * Bookmark END_LOOP_1
after transformation, the RTF document transformed contains this RTF code :
* #foreach($customers.Name in $list_customers) * $customers.Name * #end
* RTF row (RTF code before MERGEFIELD) * Mergefield list <$customers.Name> * RTF row (RTF code after MERGEFIELD)
* RTF row (RTF code before MERGEFIELD) * Bookmark START_LOOP_1 * Mergefield list <$customers.Name> * Bookmark END_LOOP_1 * RTF row (RTF code after MERGEFIELD)
* Bookmark START_LOOP_1 * RTF row (RTF code before MERGEFIELD) * Mergefield list <$customers.Name> * RTF row (RTF code after MERGEFIELD) * Bookmark END_LOOP_1