我尝试使用 openxml 替换书签的文本。它仅适用于每个段落的第一行和单个行。
我的代码:
foreach (BookmarkStart bookMarkStart in wordprocessingDocument.MainDocumentPart.RootElement.Descendants<BookmarkStart>())
{
if (bookMarkStart.Name == "signet")
{
OpenXmlElement elem = bookMarkStart.NextSibling();
while (elem != null && !(elem is BookmarkEnd))
{
OpenXmlElement nextElem = elem.NextSibling();
elem.Remove();
elem = nextElem;
}
bookMarkStart.Parent.InsertAfter<Run>(new Run(new Text("teeeest")), bookMarkStart);
}
}
下面是使用这些工具的xml文件。在这里,我有两个段落,但是只有第一个段落被替换,并且我使用id=0
作为书签,第二个段落被自动添加
<w:body>
<w:p w:rsidR="0028616D" w:rsidRDefault="005537D9">
<w:bookmarkStart w:name="signet" w:id="0" />
<w:r>
<w:t>Test1</w:t>
</w:r>
</w:p>
<w:p w:rsidR="005537D9" w:rsidRDefault="005537D9">
<w:r>
<w:t>Test2</w:t>
</w:r>
<w:bookmarkStart w:name="_GoBack" w:id="1" />
<w:bookmarkEnd w:id="0" />
<w:bookmarkEnd w:id="1" />
</w:p>
<w:sectPr w:rsidR="005537D9">
<w:pgSz w:w="11906" w:h="16838" />
<w:pgMar w:top="1417" w:right="1417" w:bottom="1417" w:left="1417" w:header="708" w:footer="708" w:gutter="0" />
<w:cols w:space="708" />
<w:docGrid w:linePitch="360" />
</w:sectPr>
</w:body>
答案 0 :(得分:0)
以下代码可精确地用于您显示的XML构造:书签在段落的开始和结尾处的 内开始和结束。还有许多其他变体,每个变体都必须明确地加以满足。
书签由起点和终点组成。您需要同时获取内容。
由于文档可以具有多个书签,并且书签可以重叠,所以有必要获取书签的Id
以便标识哪个端点与起点匹配。该名称仅出现在BookmarkStart
元素中。起始元素和结束元素都只使用Id
。
有必要确定书签的起点和终点在什么位置(以哪种结构),因为这提供了有关父元素,同级元素和子元素可以是什么的信息。对于此特定用例,由于书签的开始和结尾都在段落中,所以它们的父项都是Paragraph
元素。下面的代码通过检查Parent.LocalName
来确定这一点。
在这种情况下,将确定起点和终点的父段。为了编辑书签中所有段落的内容,创建了List
;起点的父段被添加到其中。创建了一个附加的Paragraph
对象,用于检查下一个同级段落,并检查该书签的端点。只要书签结尾不在下一个同级段落的对象中,就执行while
循环;下一个兄弟姐妹将添加到List
。
一旦所有段落(包括书签末尾)(包括书签末尾)都位于List
中,就会循环List
替换每个段落中的文本。复制第一个Run
以便保留基本段落格式。然后,所有Run
和Text
元素都将被删除,复制的Run
将附加新的文本。
最后,书签结尾设置为最后一段的结尾。
private void btnReplaceBookmarkText_Click(object sender, EventArgs e)
{
string fileNameDoc = "path name";
string bkmName = "signet";
string bkmID = "";
string parentTypeStart = "";
string parentTypeEnd = "";
using (WordprocessingDocument pkgDoc = WordprocessingDocument.Open(fileNameDoc, true))
{
Body body = pkgDoc.MainDocumentPart.Document.Body;
BookmarkStart bkmStart = body.Descendants<BookmarkStart>().Where(bkm => bkm.Name == bkmName).FirstOrDefault();
bkmID = bkmStart.Id;
BookmarkEnd bkmEnd = body.Descendants<BookmarkEnd>().Where(bkm => bkm.Id == bkmID).FirstOrDefault();
parentTypeStart = bkmStart.Parent.LocalName;
parentTypeEnd = bkmEnd.Parent.LocalName;
int counter = 0;
if (parentTypeStart == "p" && parentTypeEnd == "p")
{ //bookmark starts at a paragraph and ends within a paragraph
Paragraph bkmParaStart = (Paragraph) bkmStart.Parent;
Paragraph bkmParaEnd = (Paragraph) bkmEnd.Parent;
Paragraph bkmParaNext = (Paragraph) bkmParaStart;
List<Paragraph> paras = new List<Paragraph>();
paras.Add(bkmParaStart);
BookmarkEnd x = bkmParaNext.Descendants<BookmarkEnd>().Where(bkm => bkm.Id == bkmID).FirstOrDefault();
while (x==null)
{
Paragraph nextPara = (Paragraph) bkmParaNext.NextSibling();
if (nextPara != null)
{
paras.Add(nextPara);
bkmParaNext = (Paragraph)nextPara.Clone();
x = bkmParaNext.Descendants<BookmarkEnd>().Where(bkm => bkm.Id == bkmID).FirstOrDefault();
}
}
foreach (Paragraph para in paras)
{
string t = "changed string once more " + counter;
Run firstRun = para.Descendants<Run>().FirstOrDefault();
Run newRun = (Run) firstRun.Clone();
newRun.RemoveAllChildren<Text>();
para.RemoveAllChildren<Run>();
para.RemoveAllChildren<Text>();
para.AppendChild<Run>(newRun).AppendChild<Text>(new Text(t));
}
//After replacing the runs and text the bookmark is at the beginning
//of the paragraph, we want it at the end
BookmarkEnd newBkmEnd = new BookmarkEnd() { Id = bkmID };
Paragraph p = paras.Last<Paragraph>();
p.Descendants<BookmarkEnd>().Where(bkm => bkm.Id==bkmID).FirstOrDefault().Remove();
p.Append(newBkmEnd);
}
}
}
注意: 由于我比Word XML更熟悉Word对象模型,因此代码可能会更优化,但对我有用。