Error in Document.Range.Replace

Hi,

I have a problem que use this method (Document.Range.Replace) because the new text sometimes contains a carry return.

Are there any solution for this?

Thanks for all.

Best regards,

Hi
Thanks for your request. There is not direct method to replace text with special characters, however, you can achieve this using IReplacingCallback. I created a simple code example that demonstrates the technique:

[Test]
public void Test001()
{
    Document doc = new Document(@"Test001\in.doc");
    doc.Range.Replace(new Regex(@"test"), new ReplaceEvaluatorFindAndInsertText("Here you can insert any text\nWith special characters you want\fall of them will be properly handled"), false);
    doc.Save(@"Test001\out.doc");
}
private class ReplaceEvaluatorFindAndInsertText: IReplacingCallback
{
    public ReplaceEvaluatorFindAndInsertText(string text)
    {
        mText = text;
    }
    ///
    /// This method is called by the Aspose.Words find and replace engine for each match.
    /// This method replaces the match string, even if it spans multiple runs.
    ///
    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);
        // This array is used to store all nodes of the match for further removing.
        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);
        }
        // Create Document Buidler aond insert text
        DocumentBuilder builder = new DocumentBuilder(e.MatchNode.Document as Document);
        builder.MoveTo((Run) runs[runs.Count - 1]);
        builder.Write(mText);
        // Now remove all runs in the sequence.
        foreach(Run run in 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)
    {
        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;
    }
    private string mText;
}

Hope this helps. Please let me know if you need more information. I will be glad to help you.
Best regards,

Hi,

I use this code but I have a new problem:

e.matchNode is not the correct node and the replace is wrong.

I attach a document with this example and it is the example code:

Private Sub TestReplace()
    Dim d As Document
    d = New Document("d:\pruebas\replace.docx")

    d.Range.Replace(New Regex("[RAW.SES.PRO.LIS.TITULO]"), New ReplaceEvaluatorFindAndInsertText("First sentence" & vbCrLf & "Second sentence"), False)
    d.Save("d:\pruebas\replace2.docx")

End Sub

Public Class ReplaceEvaluatorFindAndInsertText
    Implements IReplacingCallback

    Private _Text As String
    Public Sub New(ByVal Text As String)
        _Text = Text
    End Sub

    Public Function Replacing(e As Aspose.Words.ReplacingArgs) As Aspose.Words.ReplaceAction Implements Aspose.Words.IReplacingCallback.Replacing

        If _Text.Contains(vbCrLf) Then


            'Este es un Run node que contiene el comienzo o bien la coincidencia completa.
            Dim currentNode As Node = e.MatchNode


            'El primero (y quizás sea el único) run puede contener texto antes de la coincidencia,
            'en este caso, es necesario separar el run
            If e.MatchOffset > 0 Then
                currentNode = SplitRun(CType(currentNode, Run), e.MatchOffset)

            End If

            'Este array es usado para almacenar todos los nodos de la coincidencia.
            Dim runs As ArrayList = New ArrayList

            'Encuentra todos los runs node que contienen partes de la cadena de la coincidencia
            Dim remainingLength As Integer = e.Match.Value.Length

            While (remainingLength > 0 AndAlso currentNode IsNot Nothing AndAlso currentNode.GetText.Length <= remainingLength)
                runs.Add(currentNode)
                remainingLength = remainingLength - currentNode.GetText.Length

            End While

            'Seleccionamos el siguiente Run node.
            'Realizamos el bucle porque podemos encontrar otros tipos de nodo como Bookmarks etc…
            Do
                currentNode = currentNode.NextSibling
            Loop While currentNode IsNot Nothing AndAlso currentNode.NodeType <> NodeType.Run


            'Separar el ultimo Run node que contiene la coincidencia si hay algun texto a la izquierda
            If currentNode IsNot Nothing AndAlso remainingLength > 0 Then
                SplitRun(currentNode, remainingLength)
                runs.Add(currentNode)
            End If

            'Crear un document builder y insertar el texto
            Dim builder As DocumentBuilder = New DocumentBuilder(e.MatchNode.Document)
            If runs.Count > 0 Then
                builder.MoveTo(runs(runs.Count - 1))

            Else
                builder.MoveTo(e.MatchNode)
            End If
            builder.Write(_Text)

            'Ahora borramos todos los Run nodes in secuencia
            For Each Run As Run In runs
                Run.Remove()
            Next

            'Indicamos al motor de reemplazo de Aspose de no hacer nada porque en este proceso ya se ha realizado
            'todas las tareas que necesitabamos en el reemplazo.

        End If

        Return ReplaceAction.Stop
    End Function

    '''
    '''Separa el texto de un Run node en concreto en dos Run nodes.
    '''Inserta un nuevo Run node justo después del especificado Run node.
    '''
    Private Function SplitRun(run As Run, position As Integer) As Run
        Dim afterRun As Run = run.Clone(True)
        afterRun.Text = run.Text.Substring(position)
        run.Text = run.Text.Substring(0, position)
        run.ParentNode.InsertAfter(afterRun, run)


        Return afterRun
    End Function
End Class

Hi
Thanks for your request. Here is code I used for testing and all works fine on my side:
_

Public Sub Test001()
    Dim doc As New Document("C:\Temp\replace.docx")
    doc.Range.Replace(New Regex(Regex.Escape("[RAW.SES.PRO.LIS.TITULO]")), New ReplaceEvaluatorFindAndInsertText("First sentence" & vbCrLf & "Second sentence"), False)
    doc.Save("C:\Temp\out.docx")

End Sub

Private Class ReplaceEvaluatorFindAndInsertText
    Implements IReplacingCallback
    Public Sub New(ByVal text As String)
        mText = text
    End Sub
    ''' 
    ''' This method is called by the Aspose.Words find and replace engine for each match.
    ''' This method replaces the match string, even if it spans multiple runs.
    ''' 
    Private Function IReplacingCallback_Replacing(ByVal e As ReplacingArgs) As ReplaceAction Implements IReplacingCallback.Replacing

        ' This is a Run node that contains either the beginning or the complete match.
        Dim currentNode As Node = 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 Then
            currentNode = SplitRun(CType(currentNode, Run), e.MatchOffset)

        End If
        ' This array is used to store all nodes of the match for further removing.
        Dim runs As New ArrayList()

        ' Find all runs that contain parts of the match string.
        Dim remainingLength As Integer = e.Match.Value.Length
        Do While (remainingLength > 0) AndAlso (currentNode IsNot Nothing) AndAlso (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
            Loop While (currentNode IsNot Nothing) AndAlso (currentNode.NodeType <> NodeType.Run)

        Loop
        ' Split the last run that contains the match if there is any text left.
        If (currentNode IsNot Nothing) AndAlso (remainingLength > 0) Then
            SplitRun(CType(currentNode, Run), remainingLength)
            runs.Add(currentNode)
        End If
        ' Create Document Buidler aond insert text
        Dim builder As New DocumentBuilder(e.MatchNode.Document)
        builder.MoveTo(DirectCast(runs(runs.Count - 1), Run))
        builder.Write(mText)

        ' Now remove all runs in the sequence.
        For Each run As Run In runs
            run.Remove()
        Next run
        ' Signal to the replace engine to do nothing because we have already done all what we wanted.
        Return ReplaceAction.Skip
    End Function
    ''' 
    ''' Splits text of the specified run into two runs.
    ''' Inserts the new run just after the specified run.
    ''' 
    Private Shared Function SplitRun(ByVal run As Run, ByVal position As Integer) As Run
        Dim afterRun As Run = CType(run.Clone(True), Run)
        afterRun.Text = run.Text.Substring(position)
        run.Text = run.Text.Substring(0, position)
        run.ParentNode.InsertAfter(afterRun, run)

        Return afterRun
    End Function
    Private Shared mText As String
End Class

Best regards,