Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 59 additions & 31 deletions ooxml/XWPF/Usermodel/XWPFDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1882,44 +1882,72 @@ public XWPFDocument GetXWPFDocument()
return this;
}

private static void FindAndReplaceTextInParagraph(XWPFParagraph paragraph, string oldValue, string newValue)
private static void FindAndReplaceTextInParagraph(XWPFParagraph paragraph, string oldValue, string newValue, int startPos = 0)
{
var index = paragraph?.Text.IndexOf(oldValue) ?? -1;
if (index != -1)
if(paragraph == null)
return;

string paragraphText = string.Concat(paragraph.Runs.Select(p => p.Text));

var startIndex = paragraphText.IndexOf(oldValue, startPos);
if(startIndex == -1)
return;

int firstRun = -1;
int firstIndex = -1;

int lastRun = -1;
int lastIndex = -1;

int processedRuns = 0;
int processedChars = 0;

for(; processedRuns < paragraph.Runs.Count; processedRuns++)
{
int? firstIndex = null;
int? lastIndex = null;
for (var i = 0; i < paragraph.Runs.Count; ++i)
var text = paragraph.Runs[processedRuns].Text;
if(processedChars + text.Length > startIndex)
{
if (index < paragraph.Runs[i].Text.Length)
{
if (-index <= oldValue.Length)
{
if (firstIndex == null)
{
firstIndex = i;
}
}
else
{
lastIndex = i;
break;
}
}
index -= paragraph.Runs[i].Text.Length;
}
if (lastIndex == null)
{
lastIndex = paragraph.Runs.Count - 1;
firstRun = processedRuns;
firstIndex = startIndex - processedChars;
break;
}
var newText = String.Concat(paragraph.Runs.Skip(firstIndex ?? 0).Take((lastIndex ?? 0) - (firstIndex ?? 0) + 1).Select(_ => _.Text));
newText = newText.Replace(oldValue, newValue);
paragraph.Runs[firstIndex ?? 0].SetText(newText);
for (var i = (firstIndex ?? 0) + 1; i <= lastIndex; ++i)

processedChars += text.Length;
}

int endIndex = startIndex + oldValue.Length;

for(; processedRuns < paragraph.Runs.Count; processedRuns++)
{
var text = paragraph.Runs[processedRuns].Text;
if(processedChars + text.Length > endIndex)
{
paragraph.RemoveRun((firstIndex ?? 0) + 1);
lastRun = processedRuns;
lastIndex = endIndex - processedChars;
break;
}

processedChars += text.Length;
}

var initialFirstText = paragraph.Runs[firstRun].Text;
if(firstRun == lastRun)
{
paragraph.Runs[firstRun].SetText(initialFirstText.Substring(0, firstIndex) + newValue + initialFirstText.Substring(lastIndex));
}
else
{
paragraph.Runs[firstRun].SetText(initialFirstText.Substring(0, firstIndex) + newValue);

if(lastRun != -1)
paragraph.Runs[lastRun].SetText(paragraph.Runs[lastRun].Text.Substring(lastIndex));
}

int removeTo = lastRun == -1 ? paragraph.Runs.Count : lastRun;
for(int i = firstRun + 1; i < removeTo; i++)
paragraph.RemoveRun(firstRun + 1);

FindAndReplaceTextInParagraph(paragraph, oldValue, newValue, startIndex + newValue.Length);
}

private static void FindAndReplaceTextInTable(XWPFTable table, string oldValue, string newValue)
Expand Down
40 changes: 39 additions & 1 deletion testcases/ooxml/XWPF/UserModel/TestXWPFDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public void ReplaceParagraphText()
doc.FindAndReplaceText("$replace_cell_text$", "Regel1\nRegel2\nRegel3");

//Save Word Document
XWPFDocument outputDocument = outputDocument = XWPFTestDataSamples.WriteOutAndReadBack(doc);
XWPFDocument outputDocument = XWPFTestDataSamples.WriteOutAndReadBack(doc);

//Combine all runs of all paragraphs
StringBuilder builder = new StringBuilder();
Expand Down Expand Up @@ -184,6 +184,44 @@ public void ReplaceParagraphText()

}

[Test]
public void FindAndReplaceTextInParagraph()
{
XWPFDocument doc = XWPFTestDataSamples.OpenSampleDocument("WordFindAndReplaceTextInParagraph.docx");
string initialText = string.Concat(doc.Paragraphs.Select(t => t.Text));

Dictionary<string, string> replacers = new Dictionary<string, string>
{
{"$FINALQUALIFYINGWORK_QUESTION_1_ASKING_SHORT$", "Asking1" },
{"$FINALQUALIFYINGWORK_QUESTION_1_QUESTION$", "Question1" },
{"$FINALQUALIFYINGWORK_QUESTION_2_ASKING_SHORT$", "Asking2" },
{"$FINALQUALIFYINGWORK_QUESTION_2_QUESTION$", "Question2" },
{"$STUDENT_FULL$", "На русском с пробелами" },
{"$FINALQUALIFYINGWORK_GRADE$", "5" },
{"$SIMPLE$", "Last text" },
{"$DOUBLE$", "Twice" },
};

//This is calling FindAndReplaceTextInParagraph for each paragraph in document
foreach(var replacer in replacers)
doc.FindAndReplaceText(replacer.Key, replacer.Value);

//Save Word Document
XWPFDocument outputDocument = XWPFTestDataSamples.WriteOutAndReadBack(doc);

foreach(var replacer in replacers)
initialText = initialText.Replace(replacer.Key, replacer.Value);

string savedText = string.Concat(outputDocument.Paragraphs.Select(t => t.Text));

//Check that at least replacing in string equal to result file
ClassicAssert.AreEqual(initialText, savedText);

//Check
ClassicAssert.AreEqual("Some initial text на разный манер (inserted) and so on:Asking1: Question1Asking2: Question2Result on:1. Say that На русском с пробелами with a very long sentence and one more replacer in the end for (русский язык) sure 5Triple replace with TwiceTwice и ещё одним Twice повторением.Last text",
savedText);
}

[Test]
public void TestAddPicture()
{
Expand Down
Binary file not shown.