簡體   English   中英

從 C# 中的現有 DOCX 生成 DOCX

[英]Generate DOCX from an existing DOCX in C#

我有一個需要生成 DOCX 的新項目。 我的客戶為我提供了一個現有的 DOCX,我需要用數據庫中的一些客戶數據替換一些占位符。 好像這還不夠具有挑戰性,根據使用客戶數據的某些條件,某些部分是可選的。 所以我必須提供一些邏輯來完全省略 DOCX 的某些部分。

經過太多的研究和一些 POC,我遇到了一種新方法。 我已將 DOCX 保存為 Word XML 文檔。 這將創建一個包含所有內容的大型 XML 文件,甚至圖像也被編碼為 base64。 之后,我將 XML 文件的內容復制到 T4 模板。 這樣做允許我根據客戶數據添加動態內容,並在我的代碼中生成一個 Word XML 文檔作為一個大字符串。

但現在我堅持基於 Word XML 文檔字符串再次創建 Docx。 我試過使用 OpenXml Sdk 但找不到任何關於如何做到這一點的真實文檔。 經過一些實驗,我最終得到了下面的代碼,但它沒有解析 XML(根級別的數據無效。第 1 行,position 1)。

作為第二次嘗試,我嘗試了另一篇文章的一些建議,但這導致了另一個異常(XML 的內容無效,不能構造為元素。(參數'outerXml'))

有沒有辦法做到這一點,還是我應該離開 T4 模板並嘗試另一種方法? T4 模板的另一個問題是一些圖像的大小,它會導致長的 base64 字符串生成太多行。 我想我可以在創建 XML 之前用占位符替換圖像並交換它們......

    public FileData CreateDocx(string title, string xml)
    {
        using (MemoryStream generatedDocument = new MemoryStream())
        {
            using (WordprocessingDocument package =
                WordprocessingDocument.Create(generatedDocument, WordprocessingDocumentType.Document))
            {
                var mainPart = package.AddMainDocumentPart();
                //First attempt
                //new Document(xml).Save(mainPart);

                var doc = new XmlDocument();
                doc.LoadXml(xml);
                new Document(doc.OuterXml).Save(mainPart);
            }

            return new FileData(title, generatedDocument.ToArray());
        }
    }

根據 Thomas Weller 的反饋,我試用了DocX 這個庫使打開/復制/創建 DOCX 文件變得更加容易。 經過一番研究,我完全改變了我的方法。 我最終使用現有的 DOCX 作為模板。

首先,我在需要從數據庫中注入數據的段落中添加了占位符 為此,我使用了 {{CustomerName}} 之類的東西。 通過使用replaceText ,我能夠用正確的數據交換所有占位符。

這樣做之后,我添加了部分 這可以通過使用本指南在 Word 中輕松完成。 添加部分后,我還添加了一個占位符來標記這些部分,因為您無法在 Word 中命名一個部分。 所以我最終在 {{SectionNationalCustomer}} 等部分的開頭使用了占位符 這使我可以使用Linq 查詢查找我的部分,以搜索包含我的占位符的段落的所有部分。

一旦我收集了條件部分,我就可以通過遍歷所有SectionParagraphs刪除它們來“刪除”它們。 似乎不可能完全刪除這些部分。 當該部分需要可見時,只需將占位符替換為空字符串即可。

我需要做的最后一件事是在文檔中找到正確的表格。 我通過使用新部分嘗試了與以前相同的方法。 但似乎 object 部分的表集合總是空的,即使其中有一個表。 所以我需要另一種方法。 我再次在表格的第一列中使用了一個獨特的占位符,例如 {{TableQuotation}}。 然后我只是對這些部分執行相同的操作,並通過查找具有正確占位符的段落來編寫一個Linq 查詢到 select正確的表。

畢竟,我最終得到了一些看起來與此非常相似的代碼:

using (var memoryStream = new MemoryStream())
{
    // Load  template document and make copy
    using (var template = DocX.Load("MyTemplate.docx"))
    {
        var document = template.Copy();

        //Swap placeholder with data
        document.ReplaceText("{{CustomerName}}", myData.CustomerName);

        //Hide or show section based on condition
        var section = document.Sections.FirstOrDefault(s => s.SectionParagraphs.Any(p => p.Text.StartsWith("{{SectionNationalCustomer}}")));
        if (myData.Customer.Address.National == true)
        {
            //Remove placeholder when section stays visible
            document.ReplaceText("{{SectionNationalCustomer}}", "");
        }
        else
        {
            //Remove contents of section
            foreach (var paragraph in section.SectionParagraphs)
            {
                document.RemoveParagraph(paragraph);
            }
        }

        //Find and edit table
        var table = document.Tables.FirstOrDefault(s => s.Paragraphs.Any(p => p.Text.Contains("{{TableQuotation}}")));
        document.ReplaceText("{{TableQuotation}}", "");
        table.RemoveRow(1);
        
        document.SaveAs(memoryStream);
    }

    return memoryStream.ToArray();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM