Как я могу заменить текст закладки, когда в закладке много абзацев (строк) с openxml и c # - PullRequest
0 голосов
/ 27 июня 2018

Я попытался заменить текст закладки, используя 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>

1 Ответ

0 голосов
/ 29 июня 2018

Вот код, который работает для точно конструкции 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, возможно, код мог бы быть более оптимальным, но это сработало для меня.

...