How to put strike-through font in a table cell?

I’m using Aspose.Words for java and have a table that I’m populating using mail merge.

I need to change some of the text to strike through. Can I search
for a text string in a table and change its font? For example, if
I have a cell with this text

“apple banana carrot”

is there some way to search for the word banana and change it to strike-through font?

Thanks.

Yes, it can be done in Aspose.Words. But with the current API the code required to do this is quite complex. The code example below is in C#. I will ask Konstantin to post the Java analog here too.

private void SearchAndFormatExample2()
{

    Document doc = new Document(Application.StartupPath + @"\testSearchAndFormat.doc");
    doc.Range.Replace(new Regex("banana"), new ReplaceEvaluator(SearchAndFormatEvaluator), false);
    string resultFilename = Application.StartupPath + @"\testSearchAndFormat2 Out.doc";
    // Save document to file
    doc.Save(resultFilename);
    // Open document in MS Word to check results.
    System.Diagnostics.Process.Start(resultFilename);
}
// Define the desired formatting here.
private void ApplyFormatting(Run run)
{

    run.Font.StrikeThrough = true;
}
private ReplaceAction SearchAndFormatEvaluator(object sender, ReplaceEvaluatorArgs e)
{

    Run run = (Run)e.MatchNode;
    Paragraph para = run.ParentParagraph;
    // We are using /xbf (inverted question mark) symbol for temporary purposes.
    // Any symbol will do that is not special and is guaranteed not to be present in the document.
    matchedRun = run;
    para.Range.Replace(new Regex(e.Match.Value), new ReplaceEvaluator(RunReplaceEvaluator), false);
    Run subrun = (Run)run.Clone(false);
    int pos = run.Text.IndexOf("\xbf");
    subrun.Text = subrun.Text.Substring(0, pos);
    run.Text = run.Text.Substring(pos + 1, run.Text.Length - pos - 1);
    para.ChildNodes.Insert(para.ChildNodes.IndexOf(run), subrun);
    Run selectionRun = new Run(run.Document, e.Match.Value);
    para.ChildNodes.Insert(para.ChildNodes.IndexOf(run), selectionRun);
    ApplyFormatting(selectionRun);
    // Let's remove run if it is empty.
    if (run.Text == "")
        run.Remove();
    // No replace action is necessary - we have already done what we intended to do.
    return ReplaceAction.Skip;
}
private Run matchedRun;
private ReplaceAction RunReplaceEvaluator(object sender, ReplaceEvaluatorArgs e)
{

    if ((Run)e.MatchNode == matchedRun)
    {
        e.Replacement = "\xbf";
        matchedRun = null;
        return ReplaceAction.Replace;
    }
    else
        return ReplaceAction.Skip;
}

Please let me know if this worked for you.
Best regards,

Hi, the same code ported to java:

private Run matchedRun;
@Test
public void searchAndFormatExample2() throws Exception
{
    Document doc = new Document("X:\\tmp\\testSearchAndFormat.doc");
    doc.getRange().replace(Pattern.compile("banana"), new SearchAndFormatEvaluator(), true);
    String resultFilename = "X:\\tmp\\testSearchAndFormat2 Out.doc";
    // Save document to file
    doc.save(resultFilename);
    // Open document in MS Word to check results.
    Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + resultFilename);
}
// Define the desired formatting here.
private void applyFormatting(Run run)
{
    run.getFont().setStrikeThrough(true);
    run.getFont().setBold(true);
    run.getFont().setColor(Color.BLUE);
}
private class SearchAndFormatEvaluator implements ReplaceEvaluator
{
    public int replace(Object sender, ReplaceEvaluatorArgs e) throws Exception
    {
        Run run = (Run) e.getMatchNode();
        Paragraph para = run.getParentParagraph();
        // We are using /xbf (inverted question mark) symbol for temporary purposes.
        // Any symbol will do that is not special and is guaranteed not to be present in the document.
        matchedRun = run;
        para.getRange().replace(Pattern.compile(e.getMatch().group()), new RunReplaceEvaluator(), false);
        Run subrun = (Run) run.deepClone(false);
        int pos = run.getText().indexOf("\u00bf");
        subrun.setText(subrun.getText().substring(0, pos));
        run.setText(run.getText().substring(pos + 1, run.getText().length()));
        para.getChildNodes().insert(para.getChildNodes().indexOf(run), subrun);
        Run selectionRun = new Run(run.getDocument(), e.getMatch().group());
        para.getChildNodes().insert(para.getChildNodes().indexOf(run), selectionRun);
        applyFormatting(selectionRun);
        // Let's remove run if it is empty.
        if ("".equals(run.getText()))
        {
            run.remove();
        }
        // No replace action is necessary - we have already done what we intended to do.
        return ReplaceAction.SKIP;
    }
}
private class RunReplaceEvaluator implements ReplaceEvaluator
{
    public int replace(Object sender, ReplaceEvaluatorArgs e)
    {
        if ((Run) e.getMatchNode() == matchedRun)
        {
            e.setReplacement("\u00bf");
            matchedRun = null;
            return ReplaceAction.REPLACE;
        }
        else
        {
            return ReplaceAction.SKIP;
        }
    }
}

Best Regards,

This did exactly what I needed.

Thanks!!

It turns out that the above code removes the exisitng font of the matched text and replaces it with the document’s default font.

Instead of creating a new run with a default font for the selection
run, I changed the code to clone the matched run. This preserves
the existing font and lets me apply my formatting on top of it.

I’m posting the code change here in case anyone else runs into the same issue.

Run selectionRun = (Run) run.deepClone(false); 
selectionRun.setText(rea.getMatch().group()); 
para.getChildNodes().insert(para.getChildNodes().indexOf(run), selectionRun);
applyFormatting(selectionRun);

Thank you so much for the improvement, I am sure it will be helpful for many developers.

Hello,
I’m trying to show a row in a table in strike thru font. This row will be shown along with other rows which are not shown in strikethru font. All these rows will be shown in the same table inside word document. I’m going to use ver. 9.1. Strike thru row is from datatable1 and non-strike thru rows are from datatable2. I’m planning to merge them into single table in the document using table template. Datatable1 might or might not have rows. Datatable2 will have atleast one row. If there is no row in Datatable1, I will not show any strike thru row in the document. I’m not able to follow the example you have listed here. I’m not looking for any replacement functionality. If there is a row in datatable1, I need to show it as strike thru. Please help me.
Thanks
Mahesh

Hi

Thanks for your request. I think, in your case, you can just define formatting of rows in the template. For example, you can use two regions as shown in the code example below:

// Prepare dummy data.
DataTable db1 = new DataTable("db1");
db1.Columns.Add("FirstName");
db1.Columns.Add("LastName");
db1.Columns.Add("City");
for (int i = 0; i < 10; i++)
db1.Rows.Add(new object[] { string.Format("firstName_{0}", i), string.Format("lastName_{0}", i),
string.Format("city_{0}", i) });
// Second table.
DataTable db2 = new DataTable("db2");
db2.Columns.Add("FirstName");
db2.Columns.Add("LastName");
db2.Columns.Add("City");
for (int i = 10; i < 20; i++)
db2.Rows.Add(new object[] { string.Format("firstName_{0}", i), string.Format("lastName_{0}", i),
string.Format("city_{0}", i) });
// Open tenmplate
Document doc =new Document(@"Test001\in.doc");
// Execute mail merge with region twice.
doc.MailMerge.ExecuteWithRegions(db1);
doc.MailMerge.ExecuteWithRegions(db2);
// Save result.
doc.Save(@"Test001\out.doc");

Sample template and output document are attached. Hope this helps. Please let me know in case of any issues, I will be glad to help you.
Best regards.

Thank you Alexey. It works perfect. I was trying to do with Run as you it listed in the example.
Mahesh

I am trying to do a similiar thing. I am merging data into a table in an existing word document. After the merge, I want to bold a table column (or the entire row) if an * appears in the column. The merge works fine, but I am getting errors when I use the code from SearchAndFormatExample2. I admit that I am copying code without really understanding it, so it might be a simple thing I’m missing. I have attached sample code with the errors I’m getting. I’m using VisualStudio2010 and Aspose.Words V9.3.
Judy W

Hi Judy,
Thanks for the inquiry.
Yes the code above does laydown the ground work for what you are looking to achieve. There were indeed some breaking changes to the API in Aspose.Words verison 9.2 and above. These changes are detailed in the article here.
Regarding your request to bold cells or the entire row of text which has the astrix symbol it, please see the code below which implements this. I have made some changes to the code above and implemented the new members used in the new API. Note that Regex.Escape is needed as the “*” symbol is reserved as a special character within regular expressions.

doc.MailMerge.ExecuteWithRegions(tblPremium);
doc.Range.Replace(new System.Text.RegularExpressions.Regex(Regex.Escape("*")), new HandleReplace(), false);
doc.Save("Merge out.doc");
class HandleReplace : IReplacingCallback
{
    // Define the desired formating for the cell here
    private void ApplyFormattingToCell(Cell cell)
    {
        foreach (Paragraph para in cell.Paragraphs)
        {
            foreach (Run run in para.Runs)
            {
                run.Font.Bold = true;
            }
        }
    }
    // Define the desired formating for the row here
    private void ApplyFormattingToRow(Row row)
    {
        foreach (Cell cell in row.Cells)
        {
            ApplyFormattingToCell(cell);
        }
    }
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs args)
    {
        // Cast the matched node to Run
        Run run = (Run)args.MatchNode;
        // Get the cell and row that this run is contained in
        Cell cell = (Cell)run.GetAncestor(NodeType.Cell);
        Row row = (Row)run.GetAncestor(NodeType.Row);
        // Skip replacing this match if it's not contained within a cell (and a row as well)
        if (cell == null)
            return ReplaceAction.Skip;
        // Use this method to apply bold formatting to just the cell where the astrix appears
        ApplyFormattingToCell(cell);
        // Use this method to apply bold formatting to just the entire row where the astrix appears
        ApplyFormattingToRow(row);
        return ReplaceAction.Replace;
    }
}

If you have any further queries please feel free to ask.
Thanks,

Hi Team,

I want to Apply formatting for the full row using ExecuteWithRegions.
please let me know how.

Hi,

Thanks for your inquiry. I think you can achieve what you need after using the code suggested in the following article:
https://docs.aspose.com/words/net/types-of-mail-merge-operations/

Please let us know if you need more information, we are always glad to help you.

Best Regards,