Hello,
I am currently adapting my code to the new 9.5 from 9.1. This means that the replacements are required to be changed to IReplacingCallback.
I need to use it to replace fields for PAGE, NUMPAGES and table of content. My problem now is that the first replacement (PAGE) works ok, however the two others are not entered, for no obvious reason to me.
Please advice me on what is wrong and how I can get it to work again.
Another issue I have is how can I pass parameters to the IReplaceCallback, or should I use a different approach alltogether?
Best regards,
Kees
Code:
private class ReplacePageFieldEvaluator: IReplacingCallback
{
ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
{
DocumentBuilder builder = new DocumentBuilder((Document) e.MatchNode.Document);
Run matchrun = (Run) e.MatchNode;
string runText = matchrun.Text;
Run run = (Run) e.MatchNode.Clone(true);
run.Text = runText.Substring(runText.IndexOf(e.Match.Value) + e.Match.Value.Length);
matchrun.Text = runText.Substring(0, runText.IndexOf(e.Match.Value));
e.MatchNode.ParentNode.InsertAfter(run, e.MatchNode);
builder.MoveTo(run);
if (e.Match.Value == "[PAGE]")
builder.InsertField("PAGE", null);
e.Replacement = "";
return ReplaceAction.Replace;
}
}
private class ReplaceTotalPagesFieldEvaluator: IReplacingCallback
{
ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
{
DocumentBuilder builder = new DocumentBuilder((Document) e.MatchNode.Document);
Run matchrun = (Run) e.MatchNode;
string runText = matchrun.Text;
Run run = (Run) e.MatchNode.Clone(true);
run.Text = runText.Substring(runText.IndexOf(e.Match.Value) + e.Match.Value.Length);
e.MatchNode.ParentNode.InsertAfter(run, e.MatchNode);
builder.MoveTo(run);
if (e.Match.Value == "[NUMPAGES]")
builder.InsertField("NUMPAGES", null);
e.Replacement = "";
return ReplaceAction.Replace;
}
}
private class ReplaceTableOfContentFieldEvaluator: IReplacingCallback
{
ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
{
DocumentBuilder builder = new DocumentBuilder((Document) e.MatchNode.Document);
Run matchrun = (Run) e.MatchNode;
string runText = matchrun.Text;
Run run = (Run) e.MatchNode.Clone(true);
run.Text = runText.Substring(runText.IndexOf(e.Match.Value) + e.Match.Value.Length);
e.MatchNode.ParentNode.InsertAfter(run, e.MatchNode);
builder.MoveTo(run);
if (e.Match.Value == "[TOC]")
builder.InsertTableOfContents(String.Format("\\o \"1-{0}\" \\h \\z \\u", 3));
e.Replacement = "";
return ReplaceAction.Replace;
}
}
protected void BuildDoc(Document doc, string html, bool addbody)
{
DocumentBuilder builder = new DocumentBuilder(doc);
PageLayout(builder.PageSetup);
builder.MoveToDocumentStart();
AddHeader(builder, GetPageHeader(html, true));
builder.MoveToDocumentStart();
AddFooter(builder, GetPageFooter(html, true));
builder.MoveToDocumentStart();
if (addbody)
{
builder.InsertHtml(html);
builder.MoveToDocumentStart();
}
switch (_options.Type)
{
case ReportType.Rtf:
case ReportType.Word:
UpdateTableFormatting(doc);
UpdateCellFormatting(doc);
RemoveHeaderWhiteLines(doc);
break;
}
doc.Range.Replace(new Regex(@"\[PAGE\]"), new ReplacePageFieldEvaluator(), true);
doc.Range.Replace(new Regex(@"\[NUMPAGES\]"), new ReplaceTotalPagesFieldEvaluator(), true);
builder.MoveToDocumentStart();
// if (_options.Layout.TableOfContent.include)
doc.Range.Replace(new Regex(@"\[TOC\]"), new ReplaceTableOfContentFieldEvaluator(), true);
}
Hello,
I have made changes to the ReplaceTableOfContentFieldEvaluator that I expect will work, however I still am unable to get it to execute, but once I get it to work, it would include required parameters.
Due to my focus on the strange situation of the code not executing, I totally overlooked the fact that it is a class.
As long as I am unable to get the code to actually to execute, I would appreciate also any feedback on my solution regarding the parameters.
Best regards,
Kees.
New changed code:
private class ReplaceTableOfContentFieldEvaluator : IReplacingCallback
{
public int maxlevel
{
private get;
set;
}
public string format
{
private get;
set;
}
ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
{
DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);
Run matchrun = (Run)e.MatchNode;
string runText = matchrun.Text;
Run run = (Run)e.MatchNode.Clone(true);
run.Text = runText.Substring(runText.IndexOf(e.Match.Value) + e.Match.Value.Length);
e.MatchNode.ParentNode.InsertAfter(run, e.MatchNode);
builder.MoveTo(run);
if (e.Match.Value == "[TOC]")
builder.InsertTableOfContents(String.Format(format, maxlevel));
e.Replacement = "";
return ReplaceAction.Replace;
}
}
protected void BuildDoc(Document doc, string html, bool addbody)
{
DocumentBuilder builder = new DocumentBuilder(doc);
PageLayout(builder.PageSetup);
builder.MoveToDocumentStart();
AddHeader(builder, GetPageHeader(html, true));
builder.MoveToDocumentStart();
AddFooter(builder, GetPageFooter(html, true));
builder.MoveToDocumentStart();
if (addbody)
{
builder.InsertHtml(html);
builder.MoveToDocumentStart();
}
switch (_options.Type)
{
case ReportType.Rtf:
case ReportType.Word:
UpdateTableFormatting(doc);
UpdateCellFormatting(doc);
RemoveHeaderWhiteLines(doc);
break;
}
doc.Range.Replace(new Regex(@"\[PAGE\]"), new ReplacePageFieldEvaluator(), true);
doc.Range.Replace(new Regex(@"\[NUMPAGES\]"), new ReplaceTotalPagesFieldEvaluator(), true);
builder.MoveToDocumentStart();
if (_options.Layout.TableOfContent.include)
{
ReplaceTableOfContentFieldEvaluator tocReplacer = new ReplaceTableOfContentFieldEvaluator();
tocReplacer.format = "\\o \"1-{0}\" \\h \\z \\u";
if (_options != null && _options.Layout.TableOfContent.format != null && !string.IsNullOrEmpty(_options.Layout.TableOfContent.format))
tocReplacer.format = _options.Layout.TableOfContent.format;
tocReplacer.maxlevel = _options.Layout.TableOfContent.showlevels;
doc.Range.Replace(new Regex(@"\[TOC\]"), tocReplacer, true);
}
}
Hello,
I found a solution that works for me.
Because the replacement of [PAGE] was part of [PAGE]/[NUMPAGES] it was completely removed and therefore the second search (for [NUMPAGES]) was obviously not found.
Now i have indicated for the function of [PAGE] replacement, to skip the actual replacement, this is then only done in the [NUMPAGES] replacement function.
If you have any different solution to it, please advice.
Best regards,
Kees
Let me provide you with solution how to pass parameter to to the IReplaceCallback:
private class ReplaceEvaluator : IReplacingCallback
{
private string mWord;
public ReplaceEvaluator(string word)
{
mWord = word;
}
ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
{
…
if (e.Match.Value == mWord)
…
}
}
So your call of ReplacingCallback would be like the following:
doc.Range.Replace(new Regex(@"\[PAGE\]"), new ReplaceEvaluator(word), true);
Moreover you can implement only one ReplacingCallback instead of 3 ( ReplacePageFieldEvaluator, ReplaceTotalPagesFieldEvaluator,
ReplaceTableOfContentFieldEvaluator )
In order to replace your placeholder with fields I would recommend you use the following code:
[Test]
public void Test001()
{
Document doc = new Document(@"Test001\in.docx");
doc.Range.Replace(new Regex(@"\<(?.*?)/\>"), newReplaceEvaluatorFindAndInsertMergefield(), false);
doc.Save(@"Test001\out.doc");
}
private class ReplaceEvaluatorFindAndInsertMergefield: IReplacingCallback
{
///
/// This method is called by the Aspose.Words find and replace engine for each match.
/// This method highlights 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 MergeField
DocumentBuilder builder = new DocumentBuilder(e.MatchNode.Document as Document);
builder.MoveTo((Run) runs[runs.Count - 1]);
string fieldName = e.Match.Groups["FieldName"].Value;
builder.InsertField(string.Format("MERGEFIELD {0}", fieldName), string.Format("½{0}╗", fieldName));
// 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;
}
}
Hello,
Many thanks for your feedback.
You mention “Moreover you can implement only one ReplacingCallback instead of 3 ( ReplacePageFieldEvaluator, ReplaceTotalPagesFieldEvaluator, ReplaceTableOfContentFieldEvaluator )”
does that mean that it is impossible to implement more then 1 or does it mean that your solution can be used instead of 3 individual ones?
If you are indicating that only 1 can be implemented then I would wonder how it is possible that my modifications by using the ReplaceAction.Skip resulted in exactly the behavior I was looking for.
My problem was most likely being caused by the replacement of the whole “[PAGE]/[NUMPAGES]” instead of “[PAGE]” only.
Best regards,
Kees