I have a Word document, there is a variable in the header, I want to replace this variable with FieldPage
, but when I execute the following code, the inserted FieldPage
is at the front of the Node, how can I insert it at the found position? I tried the MoveToParagraph
method, but this method seems to only move to paragraphs in the Body.
void Main()
{
var doc = new Document(@"C:\Users\54390\Desktop\demo.docx");
var finder = new FindReplaceOptions();
finder.ReplacingCallback = new WordReplaceWithPageIndexCallback();
var replaceCount = doc.Range.Replace("$num", "", finder);
doc.Save(@"C:\Users\54390\Desktop\output.docx");
}
public class WordReplaceWithPageIndexCallback : IReplacingCallback
{
public ReplaceAction Replacing(ReplacingArgs e)
{
var builder = new DocumentBuilder((Document)e.MatchNode.Document);
e.MatchNode.GetText().Dump();
builder.MoveTo(e.MatchNode);
//This following code can't move to header paragraph
//var paragraph = e.MatchNode.GetAncestor(Aspose.Words.NodeType.Paragraph);
//var index = paragraph.Document.GetChildNodes(NodeType.Paragraph, true).IndexOf(paragraph);
//builder.MoveToParagraph(index, e.MatchOffset);
builder.InsertField(Aspose.Words.Fields.FieldType.FieldPage, false);
e.Replacement = string.Empty;
return ReplaceAction.Replace;
}
}
Demo file
demo.docx (19.3 KB)
Screenshot
@sullivan There are two ways to achieve what you need.
- Replace placeholder two times.
Document doc = new Document(@"C:\Temp\in.docx");
// Replace placeholder first time to make the matched text to be in a separate Run.
doc.Range.Replace("$num", "$0", new FindReplaceOptions() { UseSubstitutions = true });
// Use WordReplaceWithPageIndexCallback to replace placeholder with a field.
FindReplaceOptions finder = new FindReplaceOptions();
finder.ReplacingCallback = new WordReplaceWithPageIndexCallback();
doc.Range.Replace("$num", "", finder);
doc.Save(@"C:\Temp\out.docx");
public class WordReplaceWithPageIndexCallback : IReplacingCallback
{
public ReplaceAction Replacing(ReplacingArgs e)
{
var builder = new DocumentBuilder((Document)e.MatchNode.Document);
builder.MoveTo(e.MatchNode);
builder.InsertField(Aspose.Words.Fields.FieldType.FieldPage, false);
e.Replacement = string.Empty;
return ReplaceAction.Replace;
}
}
- You can split the matched Run into parts in the
IReplacingCallback
implemetation:
Document doc = new Document(@"C:\Temp\in.docx");
FindReplaceOptions finder = new FindReplaceOptions();
finder.ReplacingCallback = new ReplaceEvaluatorFindAndInsertField();
// Use field code as a replacement.
doc.Range.Replace("$num", "PAGE", finder);
doc.Save(@"C:\Temp\out.docx");
internal class ReplaceEvaluatorFindAndInsertField : IReplacingCallback
{
/// <summary>
/// This method is called by the Aspose.Words find and replace engine for each match.
/// </summary>
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 deleting.
List<Run> runs = new List<Run>();
// 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((Run)currentNode);
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((Run)currentNode);
}
// Create DocumentBuilder to insert a field.
DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);
// Move builder to the first run.
builder.MoveTo(runs[0]);
// Insert field
builder.InsertField(e.Replacement);
// Delete matched runs
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;
}
private static Run SplitRun(Run run, int position)
{
Run afterRun = (Run)run.Clone(true);
run.ParentNode.InsertAfter(afterRun, run);
afterRun.Text = run.Text.Substring(position);
run.Text = run.Text.Substring((0), (0) + (position));
return afterRun;
}
}