Hello,
I am interested in doing reports using Mail Merge. My current solution in making reports simply replace text with pattern \tag-name\ with data assosiated to tag-name. How can i programically change \tag-name\ text to merge field? Other thing, what if old \tag-name\ lies in form field “IF” ,does it change anything ?
Hello, i find with forum such a solution
https://forum.aspose.com/t/62639
and i have:
public class HelloWorld
{
public static void main(String[] args)
{
try
{
Document doc = new Document("C:\zbazy.xml");
DocumentBuilder docbuilder = new DocumentBuilder(doc);
Range range = doc.getRange();
int find = range.replace(Pattern.compile("#\w+?\.\w+?#"), new ReplaceEvaluatorFindAndHighlight(doc), true);
doc.save("C:\sampleMerge.docx");
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ReplaceEvaluatorFindAndHighlight implements IReplacingCallback
{
/** This method is called by the Aspose.Words find and replace engine for each match.**
* This method highlights the match string, even if it spans multiple runs.**
*
**/
Document doc;
public ReplaceEvaluatorFindAndHighlight(Document doc)
{
this.doc = doc;
}
public int replacing(ReplacingArgs e) throws Exception
{
// This is a Run node that contains either the beginning or the complete match.
Node currentNode = e.getMatchNode();
// The first (and may be the only) run can contain text before the match,
// in this case it is necessary to split the run.
if (e.getMatchOffset()> 0)
{
currentNode = splitRun((Run) currentNode, e.getMatchOffset());
}
// This array is used to store all nodes of the match for further highlighting.
ArrayList runs = new ArrayList();
// Find all runs that contain parts of the match string.
int remainingLength = e.getMatch().group().length();
while (
(remainingLength> 0) &&
(currentNode != null) &&
(currentNode.getText().length() <= remainingLength))
{
runs.add(currentNode);
remainingLength = remainingLength - currentNode.getText().length();
// Select the next Run node.
// Have to loop because there could be other nodes such as BookmarkStart etc.
do {
currentNode = currentNode.getNextSibling();
}
while ((currentNode != null) && (currentNode.getNodeType() != NodeType.RUN));
}
// Split the last run that contains the match if there is any text left.
if ((currentNode != null) && (remainingLength> 0))
{
splitRun((Run) currentNode, remainingLength);
runs.add(currentNode);
}
DocumentBuilder builder = new DocumentBuilder(doc);
builder.moveTo((Run) runs.get(runs.size() - 1));
String fieldName = e.getMatch().group(0);
builder.insertField("MERGEFIELD" + fieldName);
// Now remove all runs in the sequence.
for (Node run : runs)
run.remove();
// Signal to the replace engine to do nothing because we have already done all what we wanted.
return ReplaceAction.SKIP;
}
/**
* Splits text of the specified run into two runs.
* Inserts the new run just after the specified run.
*/
private static Run splitRun(Run run, int position) throws Exception
{
Run afterRun = (Run) run.deepClone(true);
afterRun.setText(run.getText().substring(position));
run.setText(run.getText().substring((0), (0) + (position)));
run.getParentNode().insertAfter(afterRun, run);
return afterRun;
}
}
but with Aspose.Words.jdk16.jar and JavaSE-1.7 i encounter
java.lang.NullPointerException
at com.aspose.words.δL6.δ97(Unknown Source)
at com.aspose.words.Range.δZ(Unknown Source)
at com.aspose.words.Range.replace(Unknown Source)
at HelloWorld.main(HelloWorld.java: 44)
Somebody can help?
Hi,
Thanks for your inquiry. Please use the following code snippet to achieve your requirements. Hope this helps you.
If the problem still remains, please attach your input Word document here for testing. I will investigate the issue on my side and provide you more information.
Document doc = new Document(MyDir + "in.docx");
DocumentBuilder builder = new DocumentBuilder(doc);
DocumentReplaceCustomTags obj = new DocumentReplaceCustomTags();
doc.getRange().replace(Pattern.compile("#\\w+?\\.\\w+?#", obj, true);
Iterator iter = obj.hashtable.entrySet().iterator();
while (iter.hasNext())
{
Map.Entry mEntry = (Map.Entry) iter.next();
System.out.println(mEntry.getKey() + " : " + mEntry.getValue());
builder.moveTo((Node) mEntry.getKey());
builder.insertField(mEntry.getValue().toString());
((Node) mEntry.getKey()).remove();
}
doc.save(MyDir + "Out.docx");
public class DocumentReplaceCustomTags implements IReplacingCallback
{
public Hashtable hashtable = new Hashtable();
public int replacing(ReplacingArgs args) throws Exception
{
String tagName;
tagName = args.getMatch().group(); // Modify tagName according to your requirement e.g args.getMatch().group().substring(1, 2);
args.setReplacement(args.getReplacement() + "_Replace");
hashtable.put(args.getMatchNode(), String.format("MERGEFIELD {0} \\* MERGEFORMAT", tagName));
return ReplaceAction.REPLACE;
}
}
Hello, thank you for your brilliant answer.
My next question deals with preparing programatically document to use MailMerge with regions. I have a function that answer if tag in first row in the table is table tag( when use mail merge with regions) or plain tag when simple mail merge is enough. How to iterate through all tables in documents , check what type of tag is it( for example function isTableTag) and prepare row to use mail merge with regions ?
I would be very thankfull for any tips or answer.
Hi,
Thanks for your inquiry. I have tried to understand your query and as per my understanding you want to get the mail merge fields. Please use the MailMerge.GetFieldNames method, this method returns a collection of mail merge field names available in the document. Hope this helps you.
It would be great if you please share some more detail about your query. We will then provide you more information about your query along with code.
Hi,
My point that according to the content of first row of the table , i
will prepare prepare mail merge region of that table or not.
How to iterate through all tables, and makes mail region from
the first row?
Hi there,
Thanks for your inquiry. Please use the Document.getChildNodes method to get the collection of child nodes that match the specified type. To insert the mail merge field, please use the DocumentBuilder.insertField method as shown in following code snippet. Please read following documentation links for your kind reference.
https://reference.aspose.com/words/java/com.aspose.words/Document
https://docs.aspose.com/words/java/document-builder-overview/
https://reference.aspose.com/words/java/com.aspose.words/DocumentBuilder
https://docs.aspose.com/words/java/using-documentbuilder-to-modify-a-document/
The following code example iterate through all tables in MS Word and inert mail merge field in the first cell of first row. Hope this helps you . Please let us know if you have any more queries.
Document doc = new Document(MyDir + "in.docx");
DocumentBuilder builder = new DocumentBuilder(doc);
// Iterate through all table in the document
for (Table table: (Iterable <Table> ) doc.getChildNodes(NodeType.TABLE, true))
{
// Get first row of table
Row firstRow = table.getFirstRow();
// Get first cell of table
Cell firstCell = firstRow.getFirstCell();
// Move cursor to first cell of first row
builder.moveTo(firstCell.getFirstChild());
// Insert the mail merge field.
builder.insertField("MERGEFIELD MyFieldName \\* MERGEFORMAT");
}
doc.save(MyDir + "Out.docx");
If you face any issue, please manually create your expected Word document using Microsoft Word and attach it here for our reference. We will investigate how you want your final Word output be generated like. We will then provide you more information on this along with code.