Ms word 如何使用openxml合并具有不同标题的word文档?

Ms word 如何使用openxml合并具有不同标题的word文档?,ms-word,openxml,openxml-sdk,Ms Word,Openxml,Openxml Sdk,我正试图通过下面的例子将多个文档合并到一个文档中,正如在另一篇文章中所发布的那样。 我正在使用AltChunk AltChunk=new AltChunk()。合并文档时,似乎不会保留每个文档的单独听者。合并文档将包含合并期间第一个文档的标题。如果要合并的第一个文档不包含侦听器,则新合并文档的所有其余部分将不包含标题,反之亦然 我的问题是,如何保存合并文档的不同标题 使用系统; 使用System.IO; 使用System.Linq; 使用DocumentFormat.OpenXml.Packa

我正试图通过下面的例子将多个文档合并到一个文档中,正如在另一篇文章中所发布的那样。 我正在使用
AltChunk AltChunk=new AltChunk()
。合并文档时,似乎不会保留每个文档的单独听者。合并文档将包含合并期间第一个文档的标题。如果要合并的第一个文档不包含侦听器,则新合并文档的所有其余部分将不包含标题,反之亦然

我的问题是,如何保存合并文档的不同标题

使用系统;
使用System.IO;
使用System.Linq;
使用DocumentFormat.OpenXml.Packaging;
使用DocumentFormat.OpenXml.Wordprocessing;
命名空间WordMergeProject
{
公共课程
{
私有静态void Main(字符串[]args)
{
byte[]word1=File.ReadAllBytes(@.\..\word1.docx”);
byte[]word2=File.ReadAllBytes(@.\..\word2.docx”);
字节[]结果=合并(字1,字2);
文件.writealBytes(@.\..\word3.docx),结果;
}
专用静态字节[]合并(字节[]dest,字节[]src)
{
字符串altChunkId=“altChunkId”+DateTime.Now.Ticks.ToString();
var memoryStreamDest=新的MemoryStream();
MemoryStream dest.Write(dest,0,dest.Length);
memoryStreamDest.Seek(0,SeekOrigin.Begin);
var MemoryStream src=新的MemoryStream(src);
使用(WordprocessingDocument doc=WordprocessingDocument.Open(memoryStreamDest,true))
{
MainDocumentPart mainPart=doc.MainDocumentPart;
替代格式导入部分替代部分=
mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML,altChunkId);
altPart.FeedData(memoryStreamSrc);
var altChunk=new altChunk();
altChunk.Id=altChunkId;
openxmlement lastElem=mainPart.Document.Body.Elements().LastOrDefault();
if(lastElem==null)
{
lastElem=mainPart.Document.Body.Elements().Last();
}
//佩奇·艾因弗根
段落pageBreakP=新段落();
运行PageBreaker=新运行();
Break pageBreakBr=newbreak(){Type=BreakValues.Page};
pageBreakP.Append(pagebreaker);
pagebreaker.Append(pageBreakBr);
返回memoryStreamDest.ToArray();
}
}
}

我遇到了这个问题a并花了相当长的时间;我最终编写了一个链接到一个示例文件的问题。使用Alt Chunk实现文件与页眉和页脚的集成并非易事。我将尝试在这里介绍要点。这取决于页眉和页脚包含的内容类型(假设微软没有解决我最初遇到的任何问题)完全依靠AltChunk可能是不可能的

(还请注意,可能有一些工具/API可以处理这个问题-我不知道,在这个网站上问这个问题可能是离题的。)

背景 在解决问题之前,了解Word如何处理不同的页眉和页脚会有帮助。要了解它,请启动Word

分节符/取消页眉/页脚链接

  • 在页面上键入一些文本并插入标题
  • 将焦点移到页面的末尾,然后转到功能区中的
    页面布局
    选项卡
  • 页面设置/分页符/下一页分页符
  • 进入此页面的标题区域,注意蓝色“标记”中的信息:您将在左侧看到一个节标识符,在右侧看到“与上一个相同”。默认情况下,“与上一个相同”。要创建不同的标题,请单击标题中的“链接到上一个”按钮
因此,规则是:

需要使用未链接页眉(和/或页脚)的分节符, 以便在文档中具有不同的页眉/页脚内容

主/子文档

Word有一个名为“主控文档”的著名功能,可以将外部(“子”)文档链接到“主控”文档中。这样做会自动添加必要的分节符并取消页眉/页脚链接,以便保留原始文档

  • 转到Word的大纲视图
  • 单击“显示文档”
  • 使用“插入”插入其他文件
请注意,插入了两个分节符,一个类型为“下一页”,另一个类型为“连续”。第一个插入到传入的文件中,第二个插入到“主”文件中

插入文件时需要两个分节符,因为最后一段标记(其中包含文档结尾的分节符)没有传递到目标文档。目标文档中的分节符携带信息,以便将输入标题与目标文档中已有的标题断开链接

保存、关闭和重新打开主控文件时,子文档处于“折叠”状态(文件名为超链接,而不是内容)。可以通过返回大纲视图并单击“展开”来展开子文档按钮。要将子文档完全合并到文档中,请单击子文档旁边左上角的图标,然后单击“取消链接”

合并Word Open XML文件 因此,当合并需要保留页眉和页脚的文件时,Open XML SDK需要创建这种类型的环境。理论上,这两种方法都可以。实际上,我在只使用分节符方面遇到了问题;我从未在Word Open XML中测试过使用主文档功能

插入分节符

下面是在引入i之前插入分节符和取消标题链接的基本代码
using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

namespace WordMergeProject
{
    public class Program
    {
        private static void Main(string[] args)
        {
            byte[] word1 = File.ReadAllBytes(@"..\..\word1.docx");
            byte[] word2 = File.ReadAllBytes(@"..\..\word2.docx");

            byte[] result = Merge(word1, word2);

            File.WriteAllBytes(@"..\..\word3.docx", result);
        }

        private static byte[] Merge(byte[] dest, byte[] src)
        {
            string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString();

            var memoryStreamDest = new MemoryStream();
            memoryStreamDest.Write(dest, 0, dest.Length);
            memoryStreamDest.Seek(0, SeekOrigin.Begin);
            var memoryStreamSrc = new MemoryStream(src);

            using (WordprocessingDocument doc = WordprocessingDocument.Open(memoryStreamDest, true))
            {
                MainDocumentPart mainPart = doc.MainDocumentPart;
                AlternativeFormatImportPart altPart =
                    mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, altChunkId);
                altPart.FeedData(memoryStreamSrc);
                var altChunk = new AltChunk();
                altChunk.Id = altChunkId;
                              OpenXmlElement lastElem = mainPart.Document.Body.Elements<AltChunk>().LastOrDefault();
            if(lastElem == null)
            {
                lastElem = mainPart.Document.Body.Elements<Paragraph>().Last();
            }


            //Page Brake einfügen
            Paragraph pageBreakP = new Paragraph();
            Run pageBreakR = new Run();
            Break pageBreakBr = new Break() { Type = BreakValues.Page };

            pageBreakP.Append(pageBreakR);
            pageBreakR.Append(pageBreakBr);                

            return memoryStreamDest.ToArray();
        }
    }
}
private void btnMergeWordDocs_Click(object sender, EventArgs e)
{
    string sourceFolder = @"C:\Test\MergeDocs\";
    string targetFolder = @"C:\Test\";

    string altChunkIdBase = "acID";
    int altChunkCounter = 1;
    string altChunkId = altChunkIdBase + altChunkCounter.ToString();

    MainDocumentPart wdDocTargetMainPart = null;
    Document docTarget = null;
    AlternativeFormatImportPartType afType;
    AlternativeFormatImportPart chunk = null;
    AltChunk ac = null;
    using (WordprocessingDocument wdPkgTarget = WordprocessingDocument.Create(targetFolder + "mergedDoc.docx", DocumentFormat.OpenXml.WordprocessingDocumentType.Document, true))
    {
        //Will create document in 2007 Compatibility Mode.
        //In order to make it 2010 a Settings part must be created and a CompatMode element for the Office version set.
        wdDocTargetMainPart = wdPkgTarget.MainDocumentPart;
        if (wdDocTargetMainPart == null)
        {
            wdDocTargetMainPart = wdPkgTarget.AddMainDocumentPart();
            Document wdDoc = new Document(
                new Body(
                    new Paragraph(
                        new Run(new Text() { Text = "First Para" })),
                        new Paragraph(new Run(new Text() { Text = "Second para" })),
                        new SectionProperties(
                            new SectionType() { Val = SectionMarkValues.NextPage },
                            new PageSize() { Code = 9 },
                            new PageMargin() { Gutter = 0, Bottom = 1134, Top = 1134, Left = 1318, Right = 1318, Footer = 709, Header = 709 },
                            new Columns() { Space = "708" },
                            new TitlePage())));
            wdDocTargetMainPart.Document = wdDoc;
        }
        docTarget = wdDocTargetMainPart.Document;
        SectionProperties secPropLast = docTarget.Body.Descendants<SectionProperties>().Last();
        SectionProperties secPropNew = (SectionProperties)secPropLast.CloneNode(true);
        //A section break must be in a ParagraphProperty
        Paragraph lastParaTarget = (Paragraph)docTarget.Body.Descendants<Paragraph>().Last();
        ParagraphProperties paraPropTarget = lastParaTarget.ParagraphProperties;
        if (paraPropTarget == null)
        {
            paraPropTarget = new ParagraphProperties();
        }
        paraPropTarget.Append(secPropNew);
        Run paraRun = lastParaTarget.Descendants<Run>().FirstOrDefault();
        //lastParaTarget.InsertBefore(paraPropTarget, paraRun);
        lastParaTarget.InsertAt(paraPropTarget, 0);

        //Process the individual files in the source folder.
        //Note that this process will permanently change the files by adding a section break.
        System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(sourceFolder);
        IEnumerable<System.IO.FileInfo> docFiles = di.EnumerateFiles();
        foreach (System.IO.FileInfo fi in docFiles)
        {
            using (WordprocessingDocument pkgSourceDoc = WordprocessingDocument.Open(fi.FullName, true))
            {
                IEnumerable<HeaderPart> partsHeader = pkgSourceDoc.MainDocumentPart.GetPartsOfType<HeaderPart>();
                IEnumerable<FooterPart> partsFooter = pkgSourceDoc.MainDocumentPart.GetPartsOfType<FooterPart>();
                //If the source document has headers or footers we want to retain them.
                //This requires inserting a section break at the end of the document.
                if (partsHeader.Count() > 0 || partsFooter.Count() > 0)
                {
                    Body sourceBody = pkgSourceDoc.MainDocumentPart.Document.Body;
                    SectionProperties docSectionBreak = sourceBody.Descendants<SectionProperties>().Last();
                    //Make a copy of the document section break as this won't be imported into the target document.
                    //It needs to be appended to the last paragraph of the document
                    SectionProperties copySectionBreak = (SectionProperties)docSectionBreak.CloneNode(true);
                    Paragraph lastpara = sourceBody.Descendants<Paragraph>().Last();
                    ParagraphProperties paraProps = lastpara.ParagraphProperties;
                    if (paraProps == null)
                    {
                        paraProps = new ParagraphProperties();
                        lastpara.Append(paraProps);
                    }
                    paraProps.Append(copySectionBreak);
                }
                pkgSourceDoc.MainDocumentPart.Document.Save();
            }
            //Insert the source file into the target file using AltChunk
            afType = AlternativeFormatImportPartType.WordprocessingML;
            chunk = wdDocTargetMainPart.AddAlternativeFormatImportPart(afType, altChunkId);
            System.IO.FileStream fsSourceDocument = new System.IO.FileStream(fi.FullName, System.IO.FileMode.Open);
            chunk.FeedData(fsSourceDocument);
            //Create the chunk
            ac = new AltChunk();
            //Link it to the part
            ac.Id = altChunkId;
            docTarget.Body.InsertAfter(ac, docTarget.Body.Descendants<Paragraph>().Last());
            docTarget.Save();
            altChunkCounter += 1;
            altChunkId = altChunkIdBase + altChunkCounter.ToString();
            chunk = null;
            ac = null;
        }
    }
}
<w:body xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:p>
    <w:pPr>
      <w:pStyle w:val="Heading1" />
    </w:pPr>
    <w:subDoc r:id="rId6" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" />
  </w:p>
  <w:sectPr>
    <w:headerReference w:type="default" r:id="rId7" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" />
    <w:type w:val="continuous" />
    <w:pgSz w:w="11906" w:h="16838" />
    <w:pgMar w:top="1417" w:right="1417" w:bottom="1134" 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>
public class GeneratedClass
{
    // Creates an Body instance and adds its children.
    public Body GenerateBody()
    {
        Body body1 = new Body();

        Paragraph paragraph1 = new Paragraph();

        ParagraphProperties paragraphProperties1 = new ParagraphProperties();
        ParagraphStyleId paragraphStyleId1 = new ParagraphStyleId(){ Val = "Heading1" };

        paragraphProperties1.Append(paragraphStyleId1);
        SubDocumentReference subDocumentReference1 = new SubDocumentReference(){ Id = "rId6" };

        paragraph1.Append(paragraphProperties1);
        paragraph1.Append(subDocumentReference1);

        SectionProperties sectionProperties1 = new SectionProperties();
        HeaderReference headerReference1 = new HeaderReference(){ Type = HeaderFooterValues.Default, Id = "rId7" };
        SectionType sectionType1 = new SectionType(){ Val = SectionMarkValues.Continuous };
        PageSize pageSize1 = new PageSize(){ Width = (UInt32Value)11906U, Height = (UInt32Value)16838U };
        PageMargin pageMargin1 = new PageMargin(){ Top = 1417, Right = (UInt32Value)1417U, Bottom = 1134, Left = (UInt32Value)1417U, Header = (UInt32Value)708U, Footer = (UInt32Value)708U, Gutter = (UInt32Value)0U };
        Columns columns1 = new Columns(){ Space = "708" };
        DocGrid docGrid1 = new DocGrid(){ LinePitch = 360 };

        sectionProperties1.Append(headerReference1);
        sectionProperties1.Append(sectionType1);
        sectionProperties1.Append(pageSize1);
        sectionProperties1.Append(pageMargin1);
        sectionProperties1.Append(columns1);
        sectionProperties1.Append(docGrid1);

        body1.Append(paragraph1);
        body1.Append(sectionProperties1);
        return body1;
    }
}