I am using the search and replace functionality of Aspose.Words in Java to replace a series of flags with dynamic content. So far it’s been working great, but some of my content is html, which isn’t working as well. I utilized the code in the Search and Replace documentation, but the html is merely getting inserted before the flag, not replacing the entire flag. For instance, with a flag name of ${title} the text is being displayed as:
some information should be entered here
normal text
underlined text
title}
Is there a way that I can replace the entire flag with the dynamic html content?
@sbroadhead
Could you please ZIP and attach your input Word and HTML documents along with expected output document? We will then provide you more information about your query along with code.
Attached is a zip file containing the template, the incoming html string, and the output we are looking for. The other flags in the template seem to be working fine with the standard search and replace functionality, it’s just the html content that’s causing the problem specified above. Obviously this is not production, just an example of what we are trying to do.Archive.zip (32.9 KB)
@sbroadhead
Please use the following code example to get the desired output. Hope this helps you.
Document doc = new Document(MyDir + "incoming template.docx");
FindReplaceOptions options = new FindReplaceOptions();
options.ReplacingCallback = new ReplaceWithHtmlEvaluator();
doc.Range.Replace(@"${title}", "", options);
doc.Save(MyDir + "output.docx");
private class ReplaceWithHtmlEvaluator : IReplacingCallback
{
ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
{
// This is a Run node that contains either the beginning or the complete match.
Node currentNode = e.MatchNode;
// 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.MatchOffset > 0)
currentNode = SplitRun((Run)currentNode, e.MatchOffset);
ArrayList runs = new ArrayList();
// Find all runs that contain parts of the match string.
int remainingLength = e.Match.Value.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.NextSibling;
}
while ((currentNode != null) && (currentNode.NodeType != 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);
}
Run node = (Run)runs[0];
DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);
builder.MoveTo(node);
builder.InsertHtml(@"<p><strong>some information should be entered here</strong></p><p>normal text</p><p><u>underlined text</u></p>");
Paragraph paragraph = node.ParentParagraph;
// Now remove the matched node
foreach (Run run in runs)
{
run.Remove();
}
if (paragraph.ToString(SaveFormat.Text).Trim().Length == 0)
paragraph.Remove();
// Signal to the replace engine to do nothing because we have already done all what we wanted.
return ReplaceAction.Skip;
}
private static Run SplitRun(Run run, int position)
{
Run afterRun = (Run)run.Clone(true);
afterRun.Text = run.Text.Substring(position);
run.Text = run.Text.Substring(0, position);
run.ParentNode.InsertAfter(afterRun, run);
return afterRun;
}
}
Thank you for the quick reply. However, that is C# code isn’t it? I found similar code elsewhere on the forum, but my attempt to convert it to Java didn’t work. This is my conversion and it’s not working, it’s creating the output that I sent to you.findAndInsertHtml.java.zip (1.6 KB)
@sbroadhead
Please use the following Java code example to get the desired output.
Document doc = new Document(MyDir + "incoming template.docx");
FindReplaceOptions options = new FindReplaceOptions();
options.setReplacingCallback(new ReplaceWithHtmlEvaluator());
doc.getRange().replace("${title}", "", options);
doc.save(MyDir + "output.docx");
class ReplaceWithHtmlEvaluator implements IReplacingCallback {
DocumentBuilder builder;
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();
if(builder == null)
builder = new DocumentBuilder((Document) currentNode.getDocument());
// 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);
}
Run node = (Run)runs.get(0);
DocumentBuilder builder = new DocumentBuilder((Document)e.getMatchNode().getDocument());
builder.moveTo(node);
builder.insertHtml("<p><strong>some information should be entered here</strong></p><p>normal text</p><p><u>underlined text</u></p>");
Paragraph paragraph = node.getParentParagraph();
for (Run run : (Iterable<Run>) runs){
run.remove();
}
if (paragraph.toString(SaveFormat.TEXT).trim().length() == 0)
paragraph.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 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.setText(run.getText().substring(0, position));
run.getParentNode().insertAfter(afterRun, run);
return afterRun;
}
}
Worked perfectly, I just slightly modified it so that I can dynamically change the content. Thank you.
@sbroadhead
Thanks for your feedback. Please feel free to ask if you have any question about Aspose.Words, we will be happy to help you.