Not able to replace text(placeholder for mail merge) with image in paragraph for doc/x

We are trying to replace text (mail merge codes) with Image in paragraph (we are trying represent sub report as paragraph).
In the generated word report, image is displayed as DOT, if we drag that image to resize then we can see that image.

I am attaching both template(Template_subreport.docx
) and generated report(GeneratedReport.docx)

Template_subreport.zip (2.4 MB)

In attached generated report, the DOT after text “image” is the actual image.

Image inserts without paragraph works fine.

Version used: aspose-words-17.6

Thanks in advance.
Krishna

@kakadi,

Thanks for your inquiry. To ensure a timely and accurate response, please attach the following resources here for testing:

  • The image file.
  • Please create a standalone console application (source code without compilation errors) that helps us to reproduce your problem on our end and attach it here for testing.

As soon as you get these pieces of information ready, we’ll start investigation into your issue and provide you more information. Thanks for your cooperation.

PS: To attach these resources, please zip and upload them.

Thanks for reply.
Its quite tricky to create standalone application with our datastructure, which has lot of dependencies.
I will try to explain our report structure…
Our data structure(report) has collection of report element, where each report element can be report(subreport) itself.
We traverse through this collection to create mail merge document, by replacing all place holders(mail merge codes) in the doc.
Please check printAll() method in PnAsposeFacade class, in this method we traverse through datasource/datastructure. In traverseStructure() method please check method call to traverseSubReportStructure(), where we are handling subreport as paragraph
Also check ReplaceParagraphEvaluator class.

I am attaching all sources and image, if this not clear, I would try to create some standalone example, but that may take some time.

Let me know the possibility of having teamviewer/skype screen share.

Thanks in advance
Krishna
ReportCode.zip (2.4 MB)

@kakadi,

Thanks for your inquiry. We have tested the scenario using latest version of Aspose.Words for Java 17.9 with following code example. We have not found the shared issue. Please upgrade to the latest version of Aspose.Words for Java 17.9 and change your code as shown below. We have attached the output document with this post for your kind reference. Hope this helps you.
17.9.zip (2.4 MB)

Document doc = new Document(MyDir + "Template_subreport.docx");

Pattern regex = Pattern.compile("<Image:O13a_08>", Pattern.CASE_INSENSITIVE);
FindReplaceOptions findReplaceOptions = new FindReplaceOptions();
findReplaceOptions.setReplacingCallback(new FindAndInsertImage(MyDir + "JPG 2450 KB.JPG"));
doc.getRange().replace(regex, "", findReplaceOptions);

doc.save(MyDir + "17.9.docx");

class FindAndInsertImage implements IReplacingCallback {
    String imagepath;
    FindAndInsertImage(String path)
    {
        imagepath = path;
    }
    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());

        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((Document) e.getMatchNode().getDocument());
        builder.moveTo((Run)runs.get(0));
        builder.insertImage(imagepath);

        for (Run run : (Iterable<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  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;
    }
}

Thank you…

I tried upgrading to 17.9, but our issue is not resolved.

More details of our issue,below are the contents of our word template:

Order with with below comments:
<Order_Comment>

[Start:Data1]this is property with code <Sub_property_Code> with imageImage:O13a_08[End:Data1]

While parsing above template we process the content between [Start:Data1] and [End:Data1] as paragraph , below is the code snippet for this:

regex = Pattern.quote(LEFT_BRACKET + START_SUB + aMergeCode + RIGHT_BRACKET) + "(.*)" + Pattern.quote(LEFT_BRACKET + END_SUB + aMergeCode + RIGHT_BRACKET);
regexPattern = Pattern.compile(regex, Pattern.DOTALL);
replace(aTemplateDocument, regexPattern, new ReplaceParagraphEvaluator<DATATYPE>(aSubReport, subReportDS));

in ReplaceParagraphEvaluator we handle paragraph using below snippet:

public int replacing(final ReplacingArgs aArgs) throws PnAsposeException
{
Run run = (Run) aArgs.getMatchNode();
Font aFont = run.getFont();
Paragraph paragraph = run.getParentParagraph();
String mergeCode = null;

while (this.dataSource.hasNext())
{
  this.dataSource.getNext(); // ignore result...

  try
  {
    final List<IReportElement> reportElements = this.subReport.getReportElements();
    Node node = paragraph.deepClone(true);
    boolean hasAtleastOneElement = false;
    for (IReportElement reportElement : reportElements)
    {
      if (reportElement != null)
      {
        final IElementProperties properties = reportElement.getProperties();
        mergeCode = ((IMergeCode) properties).getMergeCode();
        this.dataSource.getBaseValueFor(reportElement);
        final Object displayValue = this.dataSource.getDisplayValueFor(reportElement);
        final Class<?> valueType = this.dataSource.getDataTypeFor(reportElement);
        String content = AbstractMailMergeFillManager.applyMask(reportElement, displayValue, valueType);
        content = PnAsposeFacade.replaceCRandCRLFwithLineBreakChar(content);
        hasAtleastOneElement = hasAtleastOneElement || ((content != null) && !content.isEmpty());
        // Get the merge code from the report element and create a regex pattern of it
        String regex = "\\<" + mergeCode + "\\>";
        Pattern regexPattern = Pattern.compile(regex);
        try
        {
          if (mergeCode.toUpperCase().startsWith(PnAsposeFacade.IMAGE_PREFIX))
          {
            FindReplaceOptions findReplaceOption = new FindReplaceOptions(FindReplaceDirection.FORWARD, new ReplaceTagsByImageEvaluator(content));
            node.getRange().replace(regexPattern, "", findReplaceOption);
          }
          else
          {
            FindReplaceOptions findReplaceOption = new FindReplaceOptions(FindReplaceDirection.FORWARD, new ReplaceTagsByTextEvaluator(content));
            node.getRange().replace(regexPattern, "", findReplaceOption);
          }
        }
        catch (Exception ex)
        {
          throw new PnAsposeException(ex);
        }
      }
    }
    if (hasAtleastOneElement)
    {
      // PLA-2231, White rows in word doc.
      // Do not add the paragraph which is empty.
      paragraph.getParentNode().insertBefore(node, paragraph);
    }
  }
  catch (Exception ex)
  {
    throw new PnAsposeException(ex);
  }
}

try
{
  paragraph.remove();
  aArgs.setReplacement("");
}
catch (Exception ex)
{
  throw new PnAsposeException(ex);
}
return ReplaceAction.REPLACE;

}

In paragraph we are trying to replace placeholder with image, as per documentation paragraph can only have Run and Shape. I guess we need to add image as Shape, I tried adding as shape, but for shape I need to specify width and Height, which we dont want.

Can you please provide an example to replace specific text in paragraph with image.

@kakadi,

Thanks for your inquiry. All text of the document is stored in runs of text. The specific text can be inside single or multiple Run node. Please use the code example shared in my previous post to get the desired output.

DocumentBuilder.InsertImage returns the Shape object. You can change shape’s size using Shape.Width and Shape.Height properties.

@tahir.manzoor

Thanks for reply.
I tried using above code mentioned by you, I still could reproduce the issue.
I created standalone application of issue. I am enclosing the standalone application(TestMailMergeWithSubReport.java) along with template(Template_subreport.docx), image used(JPG 2450 KB.JPG)
and screen shot generated document (Error_ScreenShot.PNG).

Version used: 17.9

Issue is, I see image as DOT in the generated document

Thanks in advance
Krishna
TestSubReport.zip (2.4 MB)

@kakadi,

Thanks for sharing the detail. You are inserting the image into clone node before inserting it into Document. This is the reason you are facing this issue. You need to inset the clone node into document before inserting the image into it.

In your case, there is no need to clone the node. Please use one of the following solutions to get the desired output.

Run run = (Run) aArgs.getMatchNode();
Font aFont = run.getFont();
Paragraph paragraph = (Paragraph) run.getParentNode();
Node node = paragraph.deepClone(true);
paragraph.getParentNode().insertBefore(node, paragraph);
for (Map.Entry<String, String> entry : this.dataSource.entrySet())
{
    // Get the merge code from the report element and create a regex pattern of it
    String mergeCode = entry.getKey();
    String content = entry.getValue();
    String regex = "\\<" + mergeCode + "\\>";
    Pattern regexPattern = Pattern.compile(regex);

    if (true)
    {
        FindReplaceOptions findReplaceOption = new FindReplaceOptions(FindReplaceDirection.FORWARD, new TestReplaceTagsByImageEvaluator(content));
        node.getRange().replace(regexPattern, "", findReplaceOption);
    }
    else
    {
        FindReplaceOptions findReplaceOption = new FindReplaceOptions(FindReplaceDirection.FORWARD, new ReplaceTagsByTextEvaluator(content));
        node.getRange().replace(regexPattern, "", findReplaceOption);
    }

    //paragraph.getParentNode().insertBefore(node, paragraph);
}

Run run = (Run) aArgs.getMatchNode();
    Font aFont = run.getFont();
    Paragraph paragraph = (Paragraph) run.getParentNode();

    for (Map.Entry<String, String> entry : this.dataSource.entrySet())
    {
        // Get the merge code from the report element and create a regex pattern of it
        String mergeCode = entry.getKey();
        String content = entry.getValue();
        String regex = "\\<" + mergeCode + "\\>";
        Pattern regexPattern = Pattern.compile(regex);

        if (true)
        {
            FindReplaceOptions findReplaceOption = new FindReplaceOptions(FindReplaceDirection.FORWARD, new TestReplaceTagsByImageEvaluator(content));
            paragraph.getRange().replace(regexPattern, "", findReplaceOption);
        }
        else
        {
                   FindReplaceOptions findReplaceOption = new FindReplaceOptions(FindReplaceDirection.FORWARD, new ReplaceTagsByTextEvaluator(content));
                   paragraph.getRange().replace(regexPattern, "", findReplaceOption);
        }

        //paragraph.getParentNode().insertBefore(node, paragraph);
    }

@tahir.manzoor

Thanks a lot, I could able to solve the issue by the solution suggested you,

Regards
Krishna

@kakadi,

Thanks for your feedback. You may also set the shape’s size using following code snippet in IReplacingCallback.replacing method.

Shape shape = builder.insertImage(this.baseValue);
shape.setWidth(100);
shape.setHeight(100);