管理 Android 上演示文稿中的标签和自定义数据

概述

本文档说明了 Aspose.Slides 如何在 PowerPoint 演示文稿中使用标签和自定义数据。演示文稿的特定数据可以存储为标签或自定义 XML 部分。标签是简单的键值字符串对,而自定义 XML 部分可以存储结构化的元数据和特定应用的 XML 负载。

Aspose.Slides 提供了在演示文稿、幻灯片和形状级别添加、读取、更新、审计和删除自定义 XML 部分的 API。自定义 XML 部分对于存储文档管理标识符、工作流状态、合规元数据、模板绑定数据或其他结构化应用数据等信息的集成非常有用。

演示文稿文件中的数据存储

PPTX 文件(扩展名为 .pptx)采用 PresentationML 格式存储,属于 Office Open XML 规范的一部分。Office Open XML 定义了用于存储演示文稿内容及相关数据的包结构和关系。

一个演示文稿由多个通过关系相连的部件组成。例如,幻灯片部件包含单个幻灯片的内容,并且可以通过 ISO/IEC 29500 定义的显式关系链接到其他部件。

自定义数据可以存储为标签(ITagCollection)或自定义 XML 部分(ICustomXmlPartCollection)。两者均可通过 ICustomData 接口访问。

使用自定义 XML 部分

ICustomData.getCustomXmlParts() 方法返回与特定演示文稿对象关联的自定义 XML 部分集合。例如:

  • presentation.getCustomData().getCustomXmlParts() 包含与演示文稿本身关联的自定义 XML 部分。
  • slide.getCustomData().getCustomXmlParts() 包含与特定幻灯片关联的自定义 XML 部分。
  • shape.getCustomData().getCustomXmlParts() 包含与特定形状关联的自定义 XML 部分。

需要检查演示文稿中所有自定义 XML 部分(无论关联到哪个对象)时,请使用 Presentation.getAllCustomXmlParts()

向演示文稿添加自定义 XML 部分

使用 ICustomXmlPartCollection.add 将 XML 数据添加到自定义 XML 部分集合中。XML 必须有效且非空。

以下示例向演示文稿级别的自定义数据集合添加结构化元数据:

import com.aspose.slides.*;
import java.util.UUID;

String customXmlContent =
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
    "<metadata xmlns=\"urn:example:metadata\">" +
        "<documentId>DOC-1001</documentId>" +
        "<workflowState>Draft</workflowState>" +
    "</metadata>";

Presentation presentation = new Presentation();
try {
    ICustomXmlPart customXmlPart = presentation.getCustomData().getCustomXmlParts().add(customXmlContent);

    // add 自动分配标识符。仅在需要时设置特定的 UUID。
    customXmlPart.setItemId(UUID.randomUUID());

    presentation.save("presentation_with_custom_xml.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

add 方法也可以接受字节数组或输入流形式的 XML,这在 XML 内容已经以二进制形式存在时非常有用。

向幻灯片或形状添加自定义 XML 部分

自定义 XML 数据可以关联到特定的幻灯片或形状,而不是整个演示文稿。当元数据只描述单个对象(例如模板键、外部记录标识符或绑定信息)时,这种方式非常实用。

以下示例向一个幻灯片添加一个自定义 XML 部分,并向一个形状添加另一个自定义 XML 部分:

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    slide.getCustomData().getCustomXmlParts().add(
        "<slideMetadata xmlns=\"urn:example:slides\">" +
            "<templateKey>TitleSlide</templateKey>" +
        "</slideMetadata>");

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 50, 50, 250, 80);

    shape.getTextFrame().setText("Customer data");
    shape.getCustomData().getCustomXmlParts().add(
        "<shapeMetadata xmlns=\"urn:example:shapes\">" +
            "<recordId>CRM-4281</recordId>" +
        "</shapeMetadata>");

    presentation.save("object_custom_xml.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

添加部件的层级决定了哪个对象的 getCustomData().getCustomXmlParts() 集合包含对该部件的关系。演示文稿级别的数据适用于文档范围的元数据,幻灯片级别的数据适用于属于特定幻灯片的信息,形状级别的数据适用于绑定到单个形状的元数据。

列出并审计所有自定义 XML 部分

使用 Presentation.getAllCustomXmlParts() 可检索演示文稿中的所有自定义 XML 部分。每个 ICustomXmlPart 都会公开其标识符、XML 内容以及关联的命名空间模式。

以下示例列出所有自定义 XML 部分及其命名空间模式:

import com.aspose.slides.*;

Presentation presentation = new Presentation("presentation.pptx");
try {
    for (ICustomXmlPart customXmlPart : presentation.getAllCustomXmlParts()) {
        System.out.println("ItemId: " + customXmlPart.getItemId());
        System.out.println("XML:");
        System.out.println(customXmlPart.getXmlAsString());

        for (String namespaceSchema : customXmlPart.getNamespaceSchemas()) {
            System.out.println("Namespace schema: " + namespaceSchema);
        }

        System.out.println();
    }
} finally {
    presentation.dispose();
}

ICustomXmlPart.getNamespaceSchemas() 返回与该自定义 XML 部分关联的 XML 模式。在审计包含外部系统生成 XML 的演示文稿时,这些信息很有帮助。

读取和更新 XML 内容及 ItemId

使用 ICustomXmlPart.getXmlAsString()setXmlAsString() 以 UTF-8 字符串形式处理 XML,或使用 getXmlData()setXmlData() 以原始字节形式处理 XML。

ICustomXmlPart.getItemId() 方法返回在 Office Open XML 文档中标识该自定义 XML 部分的 UUID。需要新标识符时,请使用 setItemId()

以下示例更新 XML 内容和标识符:

import com.aspose.slides.*;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

Presentation presentation = new Presentation("presentation.pptx");
try {
    ICustomXmlPart customXmlPart = presentation.getAllCustomXmlParts()[0];

    // 读取当前 XML 文本。
    String currentXmlContent = customXmlPart.getXmlAsString();
    System.out.println(currentXmlContent);

    // 将 XML 更新为 UTF-8 字符串。
    customXmlPart.setXmlAsString(
        "<metadata xmlns=\"urn:example:metadata\">" +
            "<documentId>DOC-1001</documentId>" +
            "<workflowState>Approved</workflowState>" +
        "</metadata>");

    // getXmlData 提供相同的 XML 内容,作为原始字节。
    byte[] customXmlData = customXmlPart.getXmlData();
    System.out.println(new String(customXmlData, StandardCharsets.UTF_8));

    // 根据集成需求替换标识符。
    customXmlPart.setItemId(UUID.randomUUID());

    presentation.save("updated_custom_xml.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

调用 setXmlAsStringsetXmlData 时,请提供有效且非空的 XML。根据应用程序主要使用字符串还是字节数据,选择相应的表示方式。

删除自定义 XML 部分

Aspose.Slides 提供多种方式删除自定义 XML 数据:

以下示例通过引用删除一个演示文稿级别的自定义 XML 部分:

import com.aspose.slides.*;

Presentation presentation = new Presentation("presentation.pptx");
try {
    ICustomXmlPartCollection customXmlParts = presentation.getCustomData().getCustomXmlParts();

    if (customXmlParts.size() > 0) {
        ICustomXmlPart customXmlPart = customXmlParts.get_Item(0);
        customXmlParts.remove(customXmlPart);
    }

    presentation.save("custom_xml_removed.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

如果已有 ICustomXmlPart 实例并希望直接从演示文稿中删除该部件,而不是针对特定集合操作,只需调用 customXmlPart.remove()

也可以通过索引删除项目:

presentation.getCustomData().getCustomXmlParts().removeAt(0);

清空集合中的所有自定义 XML 部分

当需要删除与特定演示文稿对象关联的所有自定义 XML 部分时,请使用 clear

import com.aspose.slides.*;

Presentation presentation = new Presentation("presentation.pptx");
try {
    presentation.getSlides().get_Item(0).getCustomData().getCustomXmlParts().clear();

    presentation.save("slide_custom_xml_cleared.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

clear 只影响所选集合。例如,清空幻灯片的集合不会影响演示文稿级别或形状级别的集合。

若要删除演示文稿中的每一个自定义 XML 部分,可遍历 getAllCustomXmlParts() 并逐个删除:

import com.aspose.slides.*;

Presentation presentation = new Presentation("presentation.pptx");
try {
    for (ICustomXmlPart customXmlPart : presentation.getAllCustomXmlParts()) {
        customXmlPart.remove();
    }

    presentation.save("all_custom_xml_removed.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

处理链接或共享的自定义 XML 部分

在 Office Open XML 演示文稿中,同一个自定义 XML 部分可能被多个演示文稿对象引用。例如,一个文件可能包含来自多个幻灯片或形状指向同一底层自定义 XML 部分的关系。

共享部件应视为一个数据对象,拥有多个引用:

  • 使用 setXmlAsStringsetXmlDatasetItemId 更新时,会修改底层的自定义 XML 部分,因而所有引用该部件的地方都会看到更新。
  • getItemId() 可用于在审计对象级别集合时识别相同的自定义 XML 部件。
  • 从特定 getCustomXmlParts() 集合中删除部件,只会将其从该集合中移除。若希望整个演示文稿中都删除该部件,请使用 ICustomXmlPart.remove()
  • 在删除或替换共享部件之前,检查对象级别的集合以确定是否还有其他幻灯片或形状仍在引用它。

add 重载会基于 XML 内容创建新的自定义 XML 部件;它们不接受已有的 ICustomXmlPart。因此,共享关系通常出现在加载已有自定义 XML 部件的演示文稿时。

以下示例按 ItemId 审计演示文稿、幻灯片和形状级别的集合,并报告被多个位置引用的部件:

import com.aspose.slides.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;

Presentation presentation = new Presentation("presentation.pptx");
try {
    Map<UUID, List<String>> referencesByItemId = new HashMap<>();

    BiConsumer<String, ICustomXmlPartCollection> registerCustomXmlParts =
        (ownerName, customXmlParts) -> {
            for (int i = 0; i < customXmlParts.size(); i++) {
                ICustomXmlPart customXmlPart = customXmlParts.get_Item(i);
                UUID itemId = customXmlPart.getItemId();

                if (!referencesByItemId.containsKey(itemId)) {
                    referencesByItemId.put(itemId, new ArrayList<>());
                }

                referencesByItemId.get(itemId).add(ownerName);
            }
        };

    registerCustomXmlParts.accept("Presentation", presentation.getCustomData().getCustomXmlParts());

    for (int slideIndex = 0; slideIndex < presentation.getSlides().size(); slideIndex++) {
        ISlide slide = presentation.getSlides().get_Item(slideIndex);
        registerCustomXmlParts.accept("Slide " + (slideIndex + 1), slide.getCustomData().getCustomXmlParts());

        for (int shapeIndex = 0; shapeIndex < slide.getShapes().size(); shapeIndex++) {
            IShape shape = slide.getShapes().get_Item(shapeIndex);
            registerCustomXmlParts.accept("Slide " + (slideIndex + 1) + ", shape " + shapeIndex, shape.getCustomData().getCustomXmlParts());
        }
    }

    for (Map.Entry<UUID, List<String>> referenceEntry : referencesByItemId.entrySet()) {
        if (referenceEntry.getValue().size() > 1) {
            System.out.println("Shared custom XML part: " + referenceEntry.getKey());

            for (String ownerName : referenceEntry.getValue()) {
                System.out.println("  Referenced by: " + ownerName);
            }
        }
    }
} finally {
    presentation.dispose();
}

在对外部系统生成的演示文稿进行自定义 XML 数据的修改或删除之前进行此类审计非常有用,因为同一元数据部件可能参与多个关系。

获取标签的值

在 Slides 中,标签对应 IDocumentProperties.getKeywords() 方法。以下示例演示如何使用 Aspose.Slides for Android via Java 获取 Presentation 中的标签值:

import com.aspose.slides.*;

Presentation presentation = new Presentation("presentation.pptx");
try {
    String keywords = presentation.getDocumentProperties().getKeywords();
} finally {
    presentation.dispose();
}

向演示文稿添加标签

Aspose.Slides 允许向演示文稿添加标签。标签通常由两部分组成:

  • 自定义属性的名称,例如 MyTag
  • 自定义属性的值,例如 My Tag Value

如果需要依据特定规则或属性对演示文稿进行分类,可以添加相应的标签。例如,若要对来自北美国家的演示文稿进行分类,可创建一个北美标签并将相应的国家名称设为其值。

以下示例演示如何使用 Aspose.Slides for Android via Java 向 Presentation 添加标签:

import com.aspose.slides.*;

Presentation presentation = new Presentation("presentation.pptx");
try {
    ITagCollection tags = presentation.getCustomData().getTags();
    tags.set_Item("MyTag", "My Tag Value");
} finally {
    presentation.dispose();
}

标签也可以为 Slide 设置:

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);
    slide.getCustomData().getTags().set_Item("tag", "value");
} finally {
    presentation.dispose();
}

或者为单个 Shape 设置:

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);
    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 10, 10, 100, 50);
    shape.getTextFrame().setText("My text");
    shape.getCustomData().getTags().set_Item("tag", "value");
} finally {
    presentation.dispose();
}

限制

通过 getCustomData().getTags() 集合添加的标签仅存储在 PowerPoint 文件中。导出为 PDF 时,它们 不会 转移到 PDF 标签结构。因此,作为标签存储的自定义标识符在 PDF 中无法检索。

解决方法:可以将自定义标识符存放在对象的 Alt Text 中(例如 shape.setAlternativeText("MyId"))。导出为 PDF 后,Alt Text 可能会出现在 PDF 标签结构中。

常见问答

我可以一次性删除演示文稿、幻灯片或形状中的所有标签吗?

可以。标签集合 支持 clear 操作,可一次性删除所有键值对。

如何仅通过标签名称删除单个标签,而无需遍历整个集合?

标签集合 上使用 remove(name) 即可按键删除标签。

如何获取全部标签名称列表以进行分析或过滤?

标签集合 上使用 getNamesOfTags 方法,它会返回所有标签名称的数组。

如何找到所有自定义 XML 部分,而不管它们存储在哪个对象上?

使用 Presentation.getAllCustomXmlParts() 可检索演示文稿中的全部自定义 XML 部分。

在更新自定义 XML 部分时,我该使用 getXmlAsString/setXmlAsString 还是 getXmlData/setXmlData

当应用程序处理 UTF-8 XML 文本时,使用 getXmlAsStringsetXmlAsString;当 XML 已以字节数组形式存在或二进制处理更方便时,使用 getXmlDatasetXmlData。两种表示方式都对应同一个自定义 XML 部分的内容。