【问题标题】:Word Interop .InsertFile result in lost stylesWord Interop .InsertFile 导致样式丢失
【发布时间】:2018-05-03 22:06:21
【问题描述】:

我有一些将一个文件合并到另一个文件的 C# 代码。这是一个非常简单的过程:

//open the files
Microsoft.Office.Interop.Word.Document file1 = winWord.Documents.Open(ref
    file1address, ReadOnly: true, Visible: false);

Microsoft.Office.Interop.Word.Document insertfile = 
     winWord.Documents.Open(ref insertfileaddress, ReadOnly: true, 
     Visible: false);

//activate file1
file1.Activate();

// insert a page break
 file1.Words.Last.
    InsertBreak(Microsoft.Office.Interop.Word.
    WdBreakType.wdPageBreak);

//move to end and insert insertfile 
Microsoft.Office.Interop.Word.Selection selection = winWord.Selection;
selection = winWord.Selection;

selection.
    EndOf(Microsoft.Office.Interop.Word.WdUnits.wdStory, 
    Microsoft.Office.Interop.Word.WdMovementType.wdMove);

selection.InsertFile(insertfileaddress.ToString(), missing, 
     missing, missing, missing);

// save under a new name
file1.SaveAs2(NewFileName);

在我的项目中,我使用了此代码两次。我第一次生成一个大约 300 页长的文件时,效果很好。

第二次要大得多 - 生成的文件超过 15,000 页 - 在这种情况下会出现问题。段落样式全部丢失 - 所有段落都通过正常。 (我关心的是标题段落保持其“标题 1”或“标题 2”或“标题 3”名称。

在这两种情况下 file1 是相同的,并且插入文件是使用 word interop 在其他地方生成的 - 生成这些文件的代码与较小和较大版本中的代码相同。文件特征和内容特征都是一样的——但我在大版本中失去了“标题”样式。

我能想到的唯一解决方案是浏览新文件并再次应用样式。

如果有人知道为什么会发生这种情况以及如何解决它,请提供帮助。

【问题讨论】:

  • 如果您以用户身份尝试此操作会发生什么?问题是否仍然存在?如果是,那么您需要首先从这个角度研究问题——如何让它在 UI 中工作。至于你的代码:你为什么要打开两个文件?我不建议打开要插入的文件...
  • 是的,问题仍然存在于较大的文件中(但对于较小的文件,它工作正常 - 样式在 insertfile() 之后仍然存在。感谢关于第二个文件的建议。从前几天开始我还尝试做一个通配符查找/替换 - 它在我的桌面上使用 word 工作。但是在互操作中使用查找对象失败(仅适用于大文件) - 我收到一条异常消息,表明该服务不可用。我最近的尝试是循环遍历段落并应用文本内容所保证的样式 - 工作但非​​常慢。仍在寻找更好的方法。
  • 但是您可以在Word中打开这个非常大的文件并且保留样式吗?一种可能更快的方法是在导入之前将其分解为较小的文档。另一种可能更快的方法是利用 WordOpenXML 文件格式,使用 Open XML SDK 来完成这项工作,而不是“互操作”。
  • 辛迪 M - 谢谢。我对文件大小进行了一些试验,但在 5,000 页仍然遇到问题后放弃了。您对使用 WordOpenXML SDK 的建议非常棒。合并现在有 2 个步骤 - 我使用 Word OpenXML 合并文件,然后使用 word interop 更新目录(这是我需要标题样式的部分原因)。循环浏览所有段落有效 - 但需要 6 多个小时才能完成,而且我需要保持 doc 处于活动状态,否则我会遇到“对象已删除”崩溃(您在其他地方的帖子有助于解决该异常)。新过程大约需要 40 分钟。 TOC 花费的时间最多
  • 很高兴您的工作令人满意!是的,TOC 需要很长时间才能处理这么大的文件。实际上,您很幸运您现在正在这样做,而不是 10 年、15 年前:当时 Word 的最大文件大小远低于您的尝试!

标签: c# ms-word interop


【解决方案1】:

正如您将在上面的 cmets 中看到的那样,我尝试了几件事。一个可行的解决方案是遍历所有段落并根据通配符匹配替换样式。这样做需要确保文档保持活动状态(我通过在循环中添加激活行来做到这一点)。但它花了6个多小时才完成——这是不合理的。

根据上面 C. Meister 的评论,我尝试了 OpenXML,这很棒。唯一的问题是 OpenXML 不运行诸如“更新目录”之类的文字功能。所以我最终得到了2种方法。一个将一个文件附加到另一个文件并保存生成的文件(样式不变!) - 使用 OpenXML。第二个在 word interop 中打开文档,更新目录并按照我想要的方式设置一些设置,然后保存文件。

这是我的代码:

{
string targetFile = "c:\\users\\me\\desktop\\targetFile.docx";
string appendThisFile = "c:\\users\\me\\desktop\\appendThisFile.docx";
string newFile = "c:\\users\\me\\desktop\\newFile.docx";

object newFileObj = newfile;  // will need this later to save file

AppendFileToEnd(targetFile, appendThisFile, newFile);

UpdateTOCplusSettings(newFile, ref winWord);
}

// This method appends  puts a page break at the end of "targetFile" and 
then appends "appendThisFile."
// This method requires adding the WordOpenXML SDK - add using nuget
internal void AppendFileToEnd(string targetFile, string appendThisFile, 
string newFile)
    {
        File.Delete(newFile);
        File.Copy(targetFile, newFile);

        using (WordprocessingDocument myDoc =
            WordprocessingDocument.Open(newFile, true))
        {
            string altChunkId = "AltChunkId1";

            MainDocumentPart mainPart = myDoc.MainDocumentPart;

            DocumentFormat.OpenXml.Wordprocessing.Paragraph para = new 
              DocumentFormat.OpenXml.Wordprocessing.Paragraph(new 
              DocumentFormat
                .OpenXml.Wordprocessing
                .Run((new DocumentFormat.OpenXml.Wordprocessing.Break() { 
                  Type = BreakValues.Page })));

            mainPart.Document.Body.InsertAfter(para, 
               mainPart.Document.Body.LastChild);

            AlternativeFormatImportPart chunk =
                mainPart.AddAlternativeFormatImportPart(
                AlternativeFormatImportPartType.WordprocessingML, 
                altChunkId);

            using (FileStream fileStream = File.Open(appendThisFile, 
                 FileMode.Open))
                 chunk.FeedData(fileStream);

            AltChunk altChunk = new AltChunk();
            altChunk.Id = altChunkId;
            mainPart.Document
                .Body
                .InsertAfter(altChunk, mainPart.Document.Body
                .Elements<DocumentFormat.OpenXml.Wordprocessing
                   .Paragraph>().Last());
           mainPart.Document.Save();
        }
    }

 // This method opens a document and updates the first Table of Contents.
 // Note that my project already has a Word Interop object, so I pass it in 
       // and use it.  If you don't have an Word object then you need to 
       // create one before opening the doc file)
 // I also turn off grammar and spelling error - which is a choice of mine      
 internal void UpdateTOCplusSettings(string filename, ref 
     Microsoft.Office.Interop.Word.Application winword)
 {
        Microsoft.Office.Interop.Word.Document wordDocument = 
            winword.Documents.Open(filename);
        wordDocument.ShowGrammaticalErrors = false;
        wordDocument.ShowSpellingErrors = false;
        wordDocument.TablesOfContents[1].Update();
        wordDocument.Save();
  }

这个过程需要 6 分钟多一点,而不是 6 小时(加上)。追加方法耗时不到一秒,TOC 更新耗时 6 分钟。保存大约需要一秒钟。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-29
    • 1970-01-01
    相关资源
    最近更新 更多