OpenXML与Word概述

OpenXML与Word概述

一、OpenXML

1.1 背景

随着信息技术的快速发展,全球范围内产生了数十亿基于传统二进制格式(如 .doc、.xls、.ppt)的文档,并且每年仍有数十亿新文档以这些格式被创建,然而这些专有格式存在以下问题:文档兼容性、长期保存和跨平台互操作等。

1.2 概述

1.2.1 Open XML定义

Office Open XML(缩写:Open XML、OpenXML、OOXML)

一种能涵盖现有文档主题中所表示功能的标准;一项针对字处理文档、演示文稿和电子表格的建议开放标准,可由多个应用程序在多个平台上自由地实施。

1.2.2 OpenXML SDK

SDK(Software Development Kit):软件开发工具包。

Open XML SDK基于System.IO.Packaging API构建而成,并提供强类型类来处理符合Open XML文件格式规范的文档。

类似于OpenXML SDK的有NPOIXceed Words for .NETDocX

1.3 标准变迁

Open XML(简称为OOXML)是可由不同平台上的多个应用程序自由实现的字处理文档、演示文稿和电子表格的开放式标准。

序号 标准名 实施规范
1 ECMA-376 MS-OE376
2 ISO/IEC 29500 MS-OI29500

1.4 分类

Open XML定义了字处理、演示文稿和电子表格文档的格式。

每种文档都是通过以下主标记语言指定的:WordprocessingMLPresentationMLSpreadsheeML

  1. Word processing document(Word):使用WordProcessingML标记进行描述。WordprocessingML文档由文章集合组成,其中每个文章是以下类型之一:

    • 主文档
    • 词汇表文档
    • 子文档
    • 页眉
    • 页脚
    • 注释
    • 框架
    • 文本框
    • 脚注或尾注
  2. Presentations (演示文稿):使用PresentationML标记进行描述。

  3. Spreadsheet workbooks(电子表格工作薄):使用SpreadsheetML 标记进行描述。

1.5 应用方向

  • 根据业务数据自动生产文档的应用程序。
  • 从文档中提取业务数据并将这些数据输入到业务应用程序中的应用程序。
  • 只对文档的一笑部分执行受限任务,但保留文档可编辑行的应用程序。
  • 为具有特殊需要的用户群(如盲人)提供辅助功能的应用程序。
  • 在各种硬件(包括移动设备)上运行的应用程序。

二、Word

2.1 Word 格式演进

序号 特性 .doc格式 .docx格式
1 时间 Word 97-2003 Word2007及其之后
2 标准 OLE复合文档技术 Office OpenXML协议

OLE复合文档技术

  • OLE:是能让应用程序创建包含不同来源的复合文档的技术。

  • 复合文档:是微软开发、用于实现COM结构化存储的文件格式,用于把多个对象内容存放在同一个硬盘文件。

Office OpenXML协议,为由Microsoft开发的一种以XML为基础并以ZIP格式压缩的电子文件规范,支持文件、表格、备忘录、幻灯片等文件格式。

2.2 .docx 文档构成

Word文档(.docx)实质上是一个包含多个XML文件和资源的ZIP压缩包,主要由以下部分组成:

|- [Content_Types].xml 允许用户确定数据包中每个部件的内容类型
|- _rels 关系部件,定义ZIP包中各个Part之间的关系
|- docProps 存放文档的属性信息
    |- app.xml 记录应用程序特定的文档属性
    |- core.xml 存储核心属性
|- word 文档的主要内容存放于此目录
    |- _rels
        |- document.xml.rels
    |- theme
    |- document.xml 文档中所有可见文字的内容和属性及不可见部分的内容和属性
    |- fontTable.xml
    |- settings.xml 存储文档的设置
    |- styles.xml
    |- webSettings.xml

其中,word文件夹内部组成:

word内部组成

提示:我们可以新建一个Word文档,更改其后缀名.docx.zip,解压缩后查看其内部结构。

2.3 .docx与.zip格式的关系

docx格式的文件本质上是一个ZIP存档,换句话说Open XML文档以包的形式存储。

包可以具有多个彼此之间存在关系的部件,部件之间的关系控制文档的类别:

  • 字处理文档:文档的包关系项包含与主文档部件(文件)的关系。
  • 演示文稿文档:文档的包关系项包含与演示文稿部件(文件)的关系。
  • 电子表格文档:文档的包关系项包含与工作薄部件(文件)的关系。

2.4 最小WordprocessingML文档

该文档包含三个部件:内容类型部件、数据包关系部件、主文档部件

  1. 内容类型部件“/[Content_Types].xml”描述其他两个必须部件的内容类型
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
    <Default Extension="rels"
    ContentType="application/vnd. openxml formats -package . relationships+xml"/>
    <Default Extension="xml"
    ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>
  1. 数据包关系部件“/_rels/ .rels”描述数据包与主文档部件之间的关系
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Id="rId1"
    Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
    Target="document.xml"/>
</Relationships>
  1. 主文档部件“/document.xml”包含文档内容
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:body>
        <w:p>
            <w: r>
                <w:t>Hello, world.</w:t>
            </w:r>
        </w:p>
    </w:body>
</w:document>

三、DocumentFormat.OpenXML包在Word文档自动化中的应用

3.1 概述

使用 Open XML SDK 中的类以编程的方式编辑字处理文档。

  • WordprocessingDocument类表示Word文档包。

创建 WordprocessingDocument对象:

var document = @"C:\Desktop\Test.docx";

using (WordprocessingDocument wordDoc = WordprocessingDocument.Create(document, WordprocessingDocumentType.Document))
{
    // Insert other code here. 
}

获取 WordprocessingDocument对象(true-读/写模式;false-读模式):

var document = @"C:\Desktop\Test.docx";

using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, true))
{
    // Insert other code here.
}

3.2 使用XML内容创建docx文档

// document = @"C:\Desktop\Test.docx";

// To create a new package as a Word document.
public static void CreateNewWordDocument(string document)
{
    using (WordprocessingDocument wordDoc = WordprocessingDocument.Create(document, WordprocessingDocumentType.Document))
    {
        // Set the content of the document so that Word can open it.
        MainDocumentPart mainPart = wordDoc.AddMainDocumentPart();

        const string docXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
                            <w:document xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
                              <w:body>
                                <w:p>
                                  <w:r>
                                    <w:t>Hello World</w:t>
                                  </w:r>
                                </w:p>
                              </w:body>
                            </w:document>";

        using (Stream stream = mainPart.GetStream())
        {
            byte[] buf = (new UTF8Encoding()).GetBytes(docXml);
            stream.Write(buf, 0, buf.Length);
        }
    }
}

3.3 从docx文档中获取文档部件的注释

// document = @"C:\Desktop\Test.docx";

// To get the contents of a document part.
public static string GetCommentsFromDocument(string document)
{
    string? comments = null;

    using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, false))
    {
        if (wordDoc is null)
        {
            throw new ArgumentNullException(nameof(wordDoc));
        }

        MainDocumentPart mainPart = wordDoc.MainDocumentPart ?? wordDoc.AddMainDocumentPart();
        WordprocessingCommentsPart WordprocessingCommentsPart = mainPart.WordprocessingCommentsPart ?? mainPart.AddNewPart<WordprocessingCommentsPart>();

        using (StreamReader streamReader = new StreamReader(WordprocessingCommentsPart.GetStream()))
        {
            comments = streamReader.ReadToEnd();
        }
    }

    return comments;
}

3.4 从docx文档中移除文档部件

// document = @"C:\Desktop\Test.docx";

// To remove a document part from a package.
public static void RemovePart(string document)
{
    using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, true))
    {
        MainDocumentPart? mainPart = wordDoc.MainDocumentPart;

        if (mainPart is not null && mainPart.DocumentSettingsPart is not null)
        {
            mainPart.DeletePart(mainPart.DocumentSettingsPart);
        }
    }
}

3.5 从docx文档中替换文档的主题

// document = @"C:\Desktop\Test.docx";
// themeFile = @"C:\Desktop\ThemeReplace.xml"

// This method can be used to replace the theme part in a package.
public static void ReplaceTheme(string document, string themeFile)
{
    using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, true))
    {
        if (wordDoc.MainDocumentPart is null || wordDoc.MainDocumentPart.Document.Body is null || wordDoc.MainDocumentPart.ThemePart is null)
        {
            throw new ArgumentNullException("MainDocumentPart and/or Body and/or ThemePart is null.");
        }

        MainDocumentPart mainPart = wordDoc.MainDocumentPart;

        // Delete the old document part.
        mainPart.DeletePart(mainPart.ThemePart);

        // Add a new document part and then add content.
        ThemePart themePart = mainPart.AddNewPart<ThemePart>();

        using (StreamReader streamReader = new StreamReader(themeFile))
        using (StreamWriter streamWriter = new StreamWriter(themePart.GetStream(FileMode.Create)))
        {
            streamWriter.Write(streamReader.ReadToEnd());
        }
    }
}

3.6 从docx文档中替换文档部件的内容

// document = @"C:\Desktop\Test.docx";

// To search and replace content in a document part.
static void SearchAndReplace(string document)
{
    using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, true))
    {
        string? docText = null;

        if (wordDoc.MainDocumentPart is null)
        {
            throw new ArgumentNullException("MainDocumentPart and/or Body is null.");
        }

        using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
        {
            docText = sr.ReadToEnd();
        }

        Regex regexText = new Regex("Hello world!");
        docText = regexText.Replace(docText, "Hi Everyone!");

        using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
        {
            sw.Write(docText);
        }
    }
}

3.7 向docx文档中添加文字

public static void OpenAndAddTextToWordDocument(string filepath, string txt)
{
    // Open a WordprocessingDocument for editing using the filepath.
    WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(filepath, true);

    if (wordprocessingDocument is null)
    {
        throw new ArgumentNullException(nameof(wordprocessingDocument));
    }

    // Assign a reference to the existing document body.
    MainDocumentPart mainDocumentPart = wordprocessingDocument.MainDocumentPart ?? wordprocessingDocument.AddMainDocumentPart();
    mainDocumentPart.Document ??= new Document();
    mainDocumentPart.Document.Body ??= mainDocumentPart.Document.AppendChild(new Body());
    Body body = wordprocessingDocument.MainDocumentPart!.Document!.Body!;

    // Add new text.
    Paragraph para = body.AppendChild(new Paragraph());
    Run run = para.AppendChild(new Run());
    run.AppendChild(new Text(txt));

    // Dispose the handle explicitly.
    wordprocessingDocument.Dispose();
}

3.8 向docx文档中添加表格

///<summary>
///string[,] data = new string[,]
///{
///    {"名称", "描述"}
///}
///<summary>
public static void AddFixedTable(string filePath, string[,] data)
{
    using (var document = WordprocessingDocument.Open(filePath, true))
    {
        var doc = document.MainDocumentPart.Document;

        Table table = doc.Body.AppendChild(new Table());

        TableProperties props = table.PrependChild(new TableProperties());

        TableBorders tableBorders = props.AppendChild(new TableBorders(
            new TopBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 },
            new BottomBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 },
            new LeftBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 },
            new RightBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 },
            new InsideHorizontalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 6 },
            new InsideVerticalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 6 }
        ));

        for (var i = 0; i < data.GetLength(0); i++)
        {
            var tr = table.AppendChild(new TableRow());

            for (var j = 0; j < data.GetLength(1); j++)
            {
                var tc = tr.AppendChild(new TableCell());
                tc.Append(new Paragraph(new Run(new Text(data[i, j].ToString()))));

                Run run = tc.Elements<Paragraph>().First().Elements<Run>().First();
                RunProperties runProperties = run.PrependChild(new RunProperties());
                runProperties.AppendChild(new FontSize()
                {
                    Val = "20"
                });
                runProperties.AppendChild(new RunFonts()
                {
                    EastAsia = "宋体"
                });

                // Bold the title
                if (i == 0)
                {
                    Bold bold = runProperties.AppendChild(new Bold());
                }

                double widthInDxa = 7.23 * 567; // 1cm ≈ 567 Dxa
                tc.AppendChild(new TableCellProperties(
                new TableCellWidth { Type = TableWidthUnitValues.Dxa, Width = widthInDxa.ToString() }));
            }
        }
    }
}

3.9 向docx文档中添加样式

public static void AddTitleSytle()
{
    using (WordprocessingDocument doc = WordprocessingDocument.Open(FilePath, true))
    {
        Styles? styles = doc?.MainDocumentPart?.StyleDefinitionsPart?.Styles ??
                doc.MainDocumentPart.AddNewPart<StyleDefinitionsPart>().Styles;

        if (styles == null)
        {
            styles = new Styles();
            doc.MainDocumentPart.StyleDefinitionsPart.Styles = styles;
        }

        Style style_title_1 = styles.AppendChild(new Style()
        {
            Type = StyleValues.Paragraph,
            StyleId = "tl1",
            CustomStyle = true
        });

        style_title_1.Append(new Aliases()
        {
            Val = "自定义标题1"
        });
        style_title_1.Append(new StyleName()
        {
            Val = "defTitle_1"
        });

        ParagraphProperties pPr_title_1 = style_title_1.AppendChild(new ParagraphProperties());
        pPr_title_1.Append(new Justification()
        {
            Val = JustificationValues.Center
        });

        pPr_title_1.Append(new OutlineLevel()
        {
            Val = 1
        });

        pPr_title_1.Append(new SpacingBetweenLines()
        {
            Line = "400",
            LineRule = LineSpacingRuleValues.Auto
        });

        StyleRunProperties styleRunProperties_title_1 = style_title_1.AppendChild(new StyleRunProperties());
        styleRunProperties_title_1.Append(new Bold());
        styleRunProperties_title_1.Append(new RunFonts()
        {
            Ascii = "Times New Roman",
            EastAsia = "黑体"
        });
        styleRunProperties_title_1.Append(new FontSize()
        {
            Val = "48"
        });
    }
}

3.10 应用样式

public static void SetParagraphStyle_Title_Level(string styleId)
{
    using (WordprocessingDocument doc = WordprocessingDocument.Open(FilePath, true))
    {
        Paragraph paragraph = doc.MainDocumentPart.Document.Body.Elements<Paragraph>().Last();

        if (paragraph.Elements<ParagraphProperties>().Count() == 0)
        {
            paragraph.PrependChild<ParagraphProperties>(new ParagraphProperties());
        }

        ParagraphProperties pPr = paragraph.ParagraphProperties;

        if (pPr.ParagraphStyleId == null)
        {
            pPr.ParagraphStyleId = new ParagraphStyleId();
        }

        pPr.ParagraphStyleId.Val = styleId;

        doc.MainDocumentPart.Document.Save();
    }
}

四、Negut包

DocumentFormat.OpenXML:包含部件和元素的所有强类型类。

DocumentFormat.OpenXML.Framework:包含启用 SDK 的基础框架。 这是从 v3.0 开始的新包,包含以前包含在 中的 DocumentFormat.OpenXml许多类型。

五、引用文章

  1. Word解析之Word内部结构:
    https://blog.csdn.net/pdfcxc/article/details/113260490

  2. 复合文档格式文件格式研究
    https://club.excelhome.net/thread-227502-1-1.html

  3. Office Open XML白皮书
    https://www.ecma-international.org/wp-content/uploads/OpenXML_White_Paper_Chinese.pdf

  4. Open XML SDK for Office
    https://learn.microsoft.com/zh-cn/office/open-xml/open-xml-sdk

  5. docx格式文档详解
    https://juejin.cn/post/7166821284087595038

posted on 2024-12-17 14:53  wubing7755  阅读(1461)  评论(1)    收藏  举报