Дублирование документа Word с использованием OpenXml и C # - PullRequest
10 голосов
/ 17 июля 2009

Я использую Word и OpenXml для обеспечения возможности слияния почты в веб-приложении C # ASP.NET:

1) Документ загружен с рядом предопределенных строк для подстановки.

2) Используя OpenXML SDK 2.0, я открываю документ Word, получаю mainDocumentPart в виде строки и выполняю подстановку с помощью Regex.

3) Затем я создаю новый документ с использованием OpenXML, добавляю новый mainDocumentPart и вставляю строку, полученную в результате подстановки, в этот mainDocumentPart.

Однако все форматирование / стили и т. Д. Теряются в новом документе.

Полагаю, я могу скопировать и добавить части «Стиль», «Определения», «Комментарии» и т. Д. По отдельности, чтобы имитировать оригинальный документ.

Однако существует ли метод, использующий Open XML для дублирования документа, позволяющий мне выполнять замены в новой копии?

Спасибо.

Ответы [ 5 ]

13 голосов
/ 31 марта 2010

Этот фрагмент кода должен копировать все части из существующего документа в новый.

using (var mainDoc = WordprocessingDocument.Open(@"c:\sourcedoc.docx", false))
using (var resultDoc = WordprocessingDocument.Create(@"c:\newdoc.docx",
  WordprocessingDocumentType.Document))
{
  // copy parts from source document to new document
  foreach (var part in mainDoc.Parts)
    resultDoc.AddPart(part.OpenXmlPart, part.RelationshipId);
  // perform replacements in resultDoc.MainDocumentPart
  // ...
}
4 голосов
/ 08 февраля 2010

Я рекомендую использовать Content Controls. Использование их для разметки областей вашего документа, где вы хотите выполнить подстановку, безусловно, самый простой способ сделать это.

Что касается дублирования документа (и сохранения всего содержимого документа, стилей и всего), то это относительно просто:

string documentURL = "full URL to your document";
byte[] docAsArray = File.ReadAllBytes(documentURL);

using (MemoryStream stream = new MemoryStream)
{
    stream.Write(docAsArray, 0, docAsArray.Length);    // THIS performs doc copy
    using (WordprocessingDocument doc = WordprocessingDocument.Open(stream, true))
    {
        // perform content control substitution here, making sure to call .Save()
        // on any documents Part's changed.
    }
    File.WriteAllBytes("full URL of your new doc to save, including .docx", stream.ToArray());
}

На самом деле найти элементы управления контентом - это очень просто, используя LINQ. В следующем примере отображаются все элементы управления содержимым простого текста (которые имеют тип SdtRun):

using (WordprocessingDocument doc = WordprocessingDocument.Open(stream, true))
{                    
    var mainDocument = doc.MainDocumentPart.Document;
    var contentControls = from sdt in mainDocument.Descendants<SdtRun>() select sdt;

    foreach (var cc in contentControls)
    {
        // drill down through the containment hierarchy to get to 
        // the contained <Text> object
        cc.SdtContentRun.GetFirstChild<Run>().GetFirstChild<Text>().Text = "my replacement string";
    }
}

Элементы <Run> и <Text> могут еще не существовать, но создать их просто, как:

cc.SdtContentRun.Append(new Run(new Text("my replacement string")));

Надеюсь, это кому-нибудь поможет. : D

2 голосов
/ 08 февраля 2010

в качестве дополнений к вышесказанному; что, возможно, более полезно, это найти элементы управления контентом, которые были помечены (используя слово GUI). Недавно я написал несколько программ, которые заполняли шаблоны документов, которые содержали элементы управления контентом с прикрепленными тегами. Чтобы найти их просто расширение вышеуказанного запроса LINQ:

var mainDocument = doc.MainDocumentPart.Document;
var taggedContentControls = from sdt in mainDocument.Descendants<SdtElement>()
                            let sdtPr = sdt.GetFirstChild<SdtProperties>()
                            let tag = (sdtPr == null ? null : sdtPr.GetFirstChild<Tag>())
                            where (tag != null)
                            select new
                            {
                                SdtElem = sdt,
                                TagName = tag.GetAttribute("val", W).Value
                            };   

Я получил этот код из другого места, но не могу вспомнить, где в данный момент; полный кредит им.

Запрос просто создает IEnumerable анонимного типа, который содержит элемент управления содержимым и связанный с ним тег в качестве свойств. Handy! * * 1006

2 голосов
/ 24 июля 2009

Я сделал несколько очень похожих вещей, но вместо использования текстовых строк замещения я использую Word Content Controls. Некоторые подробности задокументированы в следующем сообщении в блоге: SharePoint и Open Xml . Техника не специфична для SharePoint. Вы можете повторно использовать шаблон в чистом ASP.NET или других приложениях.

Кроме того, я настоятельно рекомендую вам ознакомиться с Блогом Эрика Уайта , в котором приведены советы, рекомендации и приемы, касающиеся Open Xml. В частности, ознакомьтесь с манипуляцией в памяти с сообщением Open Xml и содержанием Word, управляющим сообщениями . Я думаю, что вы найдете их гораздо более полезными в долгосрочной перспективе.

Надеюсь, это поможет.

0 голосов
/ 17 июля 2009

Когда вы смотрите на документ openxml, изменяя расширение на zip и открывая его, вы видите, что эта подпапка слова содержит папку _rels, в которой перечислены все отношения. Эти отношения указывают на части, которые вы упомянули (стиль ...). На самом деле вам нужны эти части, потому что они содержат определение форматирования. Поэтому, не копируя их, новый документ будет использовать форматирование, определенное в файле normal.dot, а не то, которое определено в исходном документе. Поэтому я думаю, что вы должны скопировать их.

...