Mark only the word changed in the replace

Hi to you all
I am evaluating the Aspose.Words for Java and I’m really liking. Clean interface and a very good documentation :slight_smile:
I have a problem in the Range#replace() function. Running through the document and changing certain words is what I already am able to do. But now, I want to mark only those words with the color red. The ReplaceEvaluatorArgs#getMatchNode() function, gives me access to the match run, but the Run may contain more text than I am replacing, making all text red.

What solution you may think is best to this problem?

Thanks for the support

Hi
Thanks for your request. Please try using the following code:

// Create regex
Pattern regex = Pattern.compile("test");
// Open document
Document doc = new Document("C:\\Temp\\in.doc");
// find placeholders
doc.getRange().replace(regex, new MyReplaceEvaluator(), true);
// Save document
doc.save("C:\\Temp\\out.doc");

===================================================================

public class MyReplaceEvaluator implements ReplaceEvaluator
{
    public synchronized int replace(Object object, ReplaceEvaluatorArgs e) throws Exception
    {
        // Get MatchNode
        Run run1 = (Run)e.getMatchNode();
        // Create two Runs
        Run run2 = (Run)run1.deepClone(true);
        Run run3 = (Run)run1.deepClone(true);
        // Get index of match value
        int index = run1.getText().indexOf(e.getMatch().group());
        // split run that contains matched text
        run3.setText(run1.getText().substring(index + e.getMatch().group().length()));
        run2.setText(run1.getText().substring(index, index + e.getMatch().group().length()));
        run1.setText(run1.getText().substring(0, index));
        // Insert runs into the document
        run1.getParentParagraph().insertAfter(run2, run1);
        run2.getParentParagraph().insertAfter(run3, run2);
        // Change formating of the matched word
        run2.getFont().setHighlightColor(Color.YELLOW);
        return ReplaceAction.SKIP;
    }
}

Hope this helps.
Best regards.

Really thanks for the reply.

Is almost exactly that.

My only problem now is when the string that goes to the run2 is not the same length as the word we are replacing.

Many thanks for the help

Hi
If you need, I can try to help you to resolve this problem. If so, please attach your document and provide me Regex you are using to search words in the document.
Best regards.

The attached text document is my Java test class and the other is my Word test file.

Many thanks for the help

Hi
Thank you for additional information. The problem occurs because matched value consists of several Run nodes. Partially you can solve this issue by using:

doc.joinRunsWithSameFormatting();

However, if Runs in your document have different formatting, then you should combine runs programmatically.
Best regards.

I changed the word “actual” in the previous post to see if there was a magical solution :-), but this is not the main issue.

In the attached document on this post, you have a good behaved document, and the length issue is still there.
If you try the script with this file, will give a StringIndexOutOfBoundsException.

Inserting nodes, only works when the string added and the string matched are the same length.

Could you try some workaround to this, please?

Once again, many thanks for the help

Hi
Thanks for your request. Please try using the following code:

Document doc = new Document("C:\\Temp\\f.docx");
MyReplaceEvaluator rep = new MyReplaceEvaluator();
Range reprange = doc.getRange();
reprange.replace(Pattern.compile("([\\wзЗаАбБйЙнНуУъЪгГвф-]+)"), rep, false);
doc.save("c:\\Temp\\ff.docx");

====================================================================

public class MyReplaceEvaluator implements ReplaceEvaluator
{
    public synchronized int replace(Object object, ReplaceEvaluatorArgs e) throws Exception
    {
        // Get MatchNode
        Run matchedRun = (Run)e.getMatchNode();
        // If word will of more than one run
        ArrayList mainTextNodes = new ArrayList();
        // Get word
        String word = e.getMatch().group();
        // This run can contain whole mathed word or part of the word
        // When run contains only part of the word IndexOf will return negative value
        // Here we search for word start index
        int index = matchedRun.getText().lastIndexOf(word);
        int wordLength = word.length();
        while(index < 0)
        {
            index = matchedRun.getText().lastIndexOf(word.substring(0, wordLength--));
        }
        // matched run can contain text before matched word, sa me should split run here
        if(index > 0)
        {
            Run beforeRun = (Run)matchedRun.deepClone(true);
            beforeRun.setText(matchedRun.getText().substring(0, index));
            matchedRun.getParentNode().insertBefore(beforeRun, matchedRun);
        }
        // If whole word is place in the mathed node
        if(matchedRun.getText().length() > index+word.length())
        {
            Run mainTextRun = (Run)matchedRun.deepClone(true);
            mainTextRun.setText(word);
            matchedRun.getParentNode().insertBefore(mainTextRun, matchedRun);
            matchedRun.setText(matchedRun.getText().substring(index+word.length()));
            mainTextNodes.add(mainTextRun);
        }
        else
        {
            // word is spanned to several Run nodes
            matchedRun.setText(matchedRun.getText().substring(index));
            mainTextNodes.add(matchedRun);
            // Get length of the part of teh word that goes to the next run
            int currentIdx = word.length() - matchedRun.getText().length();
            Run nextRun = (Run)matchedRun.getNextSibling();
            while(currentIdx > 0)
            {
                if(nextRun.getText().length()>currentIdx)
                {
                    break;
                }
                else
                {
                    mainTextNodes.add(nextRun);
                    currentIdx = currentIdx - nextRun.getText().length();
                    nextRun = (Run)nextRun.getNextSibling();
                }
            }
            // Now we should split last run in sequence, if there is text after matched word
            if(nextRun!=null)
            {
                if(nextRun.getText().length()==currentIdx)
                {
                    mainTextNodes.add(nextRun);
                }
                else
                {
                    Run lastRun = (Run)nextRun.deepClone(true);
                    lastRun.setText(nextRun.getText().substring(0, currentIdx));
                    nextRun.getParentNode().insertBefore(lastRun, nextRun);
                    mainTextNodes.add(lastRun);
                }
            }
        }
        // hilight all runs in the sequence
        for(int i=0; i<mainTextNodes.size(); i++ )
        {
            Run currentRun = (Run)mainTextNodes.get(i);
            currentRun.getFont().setHighlightColor(Color.YELLOW);
        }
        return ReplaceAction.SKIP;
    }
}

Hope this helps. Please let me know in case of any issues.
Best regards.

This was almost what i needed but I already modify it to suit my needs.

Thank You very much for the support.

I alredy convinced my boss that was worth it. Now he jus have to convince the guy with the money