Browse Source

Merge pull request #1524 from Kazbek/master

Fix XWPFDocument FindAndReplaceTextInParagraph
pull/1547/head
Tony Qu 3 months ago
committed by GitHub
parent
commit
6f26cb3192
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 90
      ooxml/XWPF/Usermodel/XWPFDocument.cs
  2. 40
      testcases/ooxml/XWPF/UserModel/TestXWPFDocument.cs
  3. 11
      testcases/test-data/document/WordFindAndReplaceTextInParagraph.docx

90
ooxml/XWPF/Usermodel/XWPFDocument.cs

@ -1882,44 +1882,72 @@ namespace NPOI.XWPF.UserModel
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)

40
testcases/ooxml/XWPF/UserModel/TestXWPFDocument.cs

@ -150,7 +150,7 @@ namespace TestCases.XWPF.UserModel
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();
@ -184,6 +184,44 @@ namespace TestCases.XWPF.UserModel
}
[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()
{

11
testcases/test-data/document/WordFindAndReplaceTextInParagraph.docx

@ -0,0 +1,11 @@
Some initial text на разный манер (inserted) and so on:
$FINALQUALIFYINGWORK_QUESTION_1_ASKING_SHORT$: $FINALQUALIFYINGWORK_QUESTION_1_QUESTION$
$FINALQUALIFYINGWORK_QUESTION_2_ASKING_SHORT$: $FINALQUALIFYINGWORK_QUESTION_2_QUESTION$
Result on:
1. Say that $STUDENT_FULL$ with a very long sentence and one more replacer in the end for (русский язык) sure $FINALQUALIFYINGWORK_GRADE$
Triple replace with $DOUBLE$$DOUBLE$ и ещё одним $DOUBLE$ повторением.
$SIMPLE$
Loading…
Cancel
Save