打开演示文档以便进行只读访问

本主题介绍如何使用 Open XML SDK for Office 中的类以编程方式打开演示文稿文档进行只读访问。

如何打开文件进行只读访问

您可能希望打开演示文稿文档来阅读幻灯片。 您可能要从幻灯片中提取信息、将幻灯片复制到幻灯片库或者列出幻灯片的标题。 在此类情况下,您可能希望在执行这些操作的同时确保文档保持不变。 为此,可以打开文档进行只读访问。 本操作方法主题讨论以编程方式打开只读演示文稿文档的几种方法。

创建 PresentationDocument 类的实例

在 Open XML SDK 中 PresentationDocument , 类表示演示文稿文档包。 若要处理演示文稿文档,请先创建 类的 PresentationDocument 实例,然后使用该实例。 若要从文档创建类实例, Open 请调用 方法之一。 提供了多个 Open 方法,每个方法具有不同的签名。 下表包含可用于打开包的方法的重载 Open 子集。

名称 说明
Open 从指定文件创建 类的新实例 PresentationDocument
Open 从 I/O 流创建 类的新实例 PresentationDocument
Open 从指定的包创建 类的新实例 PresentationDocument

上表包含两 Open 种方法,这些方法接受布尔值作为第二个参数,以指定文档是否可编辑。 若要打开文档进行只读访问,请指定此参数的值 false

例如,可以只读方式打开演示文稿文件,并将其分配给对象, PresentationDocument 如以下 using 语句所示。 在此代码中 presentationFile , 参数是一个字符串,表示要从中打开文档的文件的路径。

    using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFilePath, false))
    {
        // Insert other code here.
    }

还可以使用 方法的第二个重载 Open (在上表中)基于 I/O 流创建 类的 PresentationDocument 实例。 如果你有一个使用流 I/O 的 Microsoft SharePoint Foundation 2010 应用程序,并且想要使用 Open XML SDK 来处理文档,则可以使用此方法。 以下代码段基于流打开文档。

    Stream stream = File.Open(strDoc, FileMode.Open);
    using (PresentationDocument presentationDocument = PresentationDocument.Open(stream, false)) 
    {
        // Place other code here.
    }

假设你有一个应用程序,该应用程序在 .NET Framework 类库的命名空间中System.IO.Packaging采用 Open XML 支持,并且你希望使用 Open XML SDK 来处理包只读。 Open XML SDK 包括接受 作为唯一参数的方法重载 Package 。 没有布尔参数指明是否应该打开文档以进行编辑。 建议的方法是在创建 类的 PresentationDocument 实例之前以只读方式打开包。 下面的代码段执行此操作。

    Package presentationPackage = Package.Open(filepath, FileMode.Open, FileAccess.Read);
    using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationPackage))
    {
        // Other code goes here.
    }

基本演示文稿文档结构

文档的基本文档结构PresentationML由多个部分组成,其中包括包含表示定义的main部分。 ISO/IEC 29500 规范中的以下文本介绍了包的整体PresentationML形式。

包的main部分PresentationML以演示文稿根元素开头。 该元素包含演示文稿,演示文稿又引用幻灯片 列表、幻灯片母版 列表、备注母版 列表和讲义母版 列表。 幻灯片列表指的是演示文稿中的所有幻灯片;幻灯片母版列表指的是演示文稿中使用的全部幻灯片母版;备注母版包含有关备注页格式的信息;讲义母版描述讲义的外观。

讲义 是可提供给访问群体 的一组打印的幻灯片。

除了文本和图形,每个幻灯片还可以包含注释备注,可以具有布局,并且可以是一个或多个自定义演示文稿 的组成部分。 注释是供维护演示文稿幻灯片平台的人员参考的批注。 备注是供演示者或访问群体参考的提醒信息或一段文字。

文档可以包括以下其他功能 PresentationML动画音频视频和幻灯片之间的 切换 效果。

文档 PresentationML 不作为一个大正文存储在单个部件中。 而实现某些功能组合的元素会存储在各个部件中。 例如,文档中的所有作者都存储在一个作者部件中,而每个幻灯片都有自己的部分。

ISO/IEC 29500:2016

以下 XML 代码示例代表包含用 ID 267 和 256 表示的两个幻灯片的演示文稿。

    <p:presentation xmlns:p="…" … > 
       <p:sldMasterIdLst>
          <p:sldMasterId
             xmlns:rel="https://…/relationships" rel:id="rId1"/>
       </p:sldMasterIdLst>
       <p:notesMasterIdLst>
          <p:notesMasterId
             xmlns:rel="https://…/relationships" rel:id="rId4"/>
       </p:notesMasterIdLst>
       <p:handoutMasterIdLst>
          <p:handoutMasterId
             xmlns:rel="https://…/relationships" rel:id="rId5"/>
       </p:handoutMasterIdLst>
       <p:sldIdLst>
          <p:sldId id="267"
             xmlns:rel="https://…/relationships" rel:id="rId2"/>
          <p:sldId id="256"
             xmlns:rel="https://…/relationships" rel:id="rId3"/>
       </p:sldIdLst>
           <p:sldSz cx="9144000" cy="6858000"/>
       <p:notesSz cx="6858000" cy="9144000"/>
    </p:presentation>

使用 Open XML SDK,可以使用对应于 PresentationML 元素的强类型类创建文档结构和内容。 可以在 命名空间中找到 这些类。 下表列出了对应于 、、 sldLayoutsldMasternotesMaster 元素的类的sld类名。

PresentationML 元素 Open XML SDK 类 说明
<sld/> Slide 演示文稿幻灯片。 它是 SlidePart 的根元素。
<sldLayout/> SlideLayout 幻灯片版式。 它是 SlideLayoutPart 的根元素。
<sldMaster/> SlideMaster 幻灯片母版。 它是 SlideMasterPart 的根元素。
<notesMaster/> NotesMaster 备注母版(或讲义母版)。 它是 NotesMasterPart 的根元素。

示例代码的工作方式

在示例代码中,在 语句中 using 打开演示文稿文档进行只读访问后,实例化 PresentationPart,然后打开幻灯片列表。 然后获取第一个幻灯片的关系 ID。

// Get the relationship ID of the first slide.
PresentationPart? part = ppt.PresentationPart;
OpenXmlElementList slideIds = part?.Presentation?.SlideIdList?.ChildElements ?? default;

// If there are no slide IDs then there are no slides.
if (slideIds.Count == 0)
{
    sldText = "";
    return;
}

string? relId = (slideIds[index] as SlideId)?.RelationshipId;

if (relId is null)
{
    sldText = "";
    return;
}

从关系 ID relId获取幻灯片部件,然后使用 生成文本字符串 StringBuilder来获取幻灯片的内部文本。

// Get the slide part from the relationship ID.
SlidePart slide = (SlidePart)part!.GetPartById(relId);

// Build a StringBuilder object.
StringBuilder paragraphText = new StringBuilder();

// Get the inner text of the slide:
IEnumerable<A.Text> texts = slide.Slide.Descendants<A.Text>();
foreach (A.Text text in texts)
{
    paragraphText.Append(text.Text);
}
sldText = paragraphText.ToString();

幻灯片的内部文本(该方法out的参数GetSlideIdAndText)将传回要显示的 main 方法。

重要

[!重要信息] 此示例仅显示演示文稿文件中的文本。 不显示非文本部件,如形状或图形。

示例代码

下面的示例打开演示文稿文件以进行只读访问并获取指定索引处的幻灯片的内部文本。 若要调用 方法 GetSlideIdAndText ,请传入演示文稿文档的完整路径。 此外,out传入参数 ,该参数sldText将在方法本身中分配一个值,然后可以在 main 程序中显示其值。 例如,对 方法的以下调用 GetSlideIdAndText 从作为参数传递给应用程序的索引和文件路径中获取演示文稿文件中的内部文本。

提示

此程序中最预期的异常是 ArgumentOutOfRangeException 异常。 例如,如果您的文件具有两个幻灯片,但是您希望显示编号为 4 的幻灯片中的文本,则可能会引发该异常。 因此,在调用 方法时,最好使用 try 块, GetSlideIdAndText 如以下示例所示。

try
{
    string file = args[0];
    bool isInt = int.TryParse(args[1], out int i);

    if (isInt)
    {
        GetSlideIdAndText(out string sldText, file, i);
        Console.WriteLine($"The text in slide #{i + 1} is {sldText}");
    }
}
catch(ArgumentOutOfRangeException exp) {
    Console.Error.WriteLine(exp.Message);
}

以下是用 C# 和 Visual Basic 编写的完整代码列表。

static void GetSlideIdAndText(out string sldText, string docName, int index)
{
    using (PresentationDocument ppt = PresentationDocument.Open(docName, false))
    {
        // Get the relationship ID of the first slide.
        PresentationPart? part = ppt.PresentationPart;
        OpenXmlElementList slideIds = part?.Presentation?.SlideIdList?.ChildElements ?? default;

        // If there are no slide IDs then there are no slides.
        if (slideIds.Count == 0)
        {
            sldText = "";
            return;
        }

        string? relId = (slideIds[index] as SlideId)?.RelationshipId;

        if (relId is null)
        {
            sldText = "";
            return;
        }

        // Get the slide part from the relationship ID.
        SlidePart slide = (SlidePart)part!.GetPartById(relId);

        // Build a StringBuilder object.
        StringBuilder paragraphText = new StringBuilder();

        // Get the inner text of the slide:
        IEnumerable<A.Text> texts = slide.Slide.Descendants<A.Text>();
        foreach (A.Text text in texts)
        {
            paragraphText.Append(text.Text);
        }
        sldText = paragraphText.ToString();
    }
}