在 JavaScript 中管理演示文稿主题

介绍

演示文稿主题定义了一套协调一致的颜色、字体、背景样式、填充、线条和效果。支持主题的对象引用这些共享定义,而不是将每个视觉属性存储为固定值,因此更改主题时可以一次性更新多个对象。

在 Aspose.Slides 中,演示文稿级别的主题可通过 Presentation.getMasterTheme 获取。演示文稿还可以在更低级别包含主题覆盖。母版可以通过 MasterThemeManager.getOverrideTheme 覆盖演示文稿主题,而布局或单个幻灯片可以通过 BaseOverrideThemeManager.getOverrideTheme 覆盖其继承的主题。实际上,幻灯片的有效主题通过以下继承链解析:演示文稿主题 → 母版覆盖 → 布局覆盖 → 幻灯片覆盖。

主题组件:颜色、字体、背景样式和效果

下面的章节展示了最常见的主题工作流:检查主题、修改颜色和字体、复制或应用主题、更新背景和效果样式,以及在继承和覆盖解析后读取有效值。

检查主题

MasterTheme 对象通过 MasterTheme.getColorScheme、MasterTheme.getFontScheme 和 MasterTheme.getFormatScheme 暴露主题的配色方案、字体方案和格式方案。在更改它们之前检查这些集合尤其有用,因为来自外部来源的演示文稿可能在样式条目数量和内容上各不相同。

以下示例读取主要主题属性并报告主题中存储了多少背景、填充、线条和效果样式:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");

const presentation = new aspose.slides.Presentation("input.pptx");
try {
    const theme = presentation.getMasterTheme();
    console.log("Theme name: " + theme.getName());
    console.log("Accent 1: " + theme.getColorScheme().getAccent1().getColor());
    console.log("Major Latin font: " + theme.getFontScheme().getMajor().getLatinFont().getFontName());
    console.log("Minor Latin font: " + theme.getFontScheme().getMinor().getLatinFont().getFontName());
    console.log("Background fill styles: " + theme.getFormatScheme().getBackgroundFillStyles().size());
    console.log("Fill styles: " + theme.getFormatScheme().getFillStyles().size());
    console.log("Line styles: " + theme.getFormatScheme().getLineStyles().size());
    console.log("Effect styles: " + theme.getFormatScheme().getEffectStyles().size());
} finally {
    presentation.dispose();
}

如果文件使用了多个母版,请不要假设每张幻灯片都有相同的有效主题。检查与幻灯片关联的母版,并在可能存在布局或幻灯片覆盖时使用本文后面展示的有效主题工作流。

更改主题颜色

主题感知的填充、线条和文本可以引用 SchemeColor 枚举中的逻辑颜色。当你在 ColorScheme 中更改相应条目时,所有仍然引用该主题颜色的对象都会使用新值解析。直接使用 RGB 颜色的对象不会因主题颜色更新而改变。

以下端到端示例创建一个使用 Accent4 的形状,将主题的 Accent4 颜色改为红色,保存演示文稿,重新打开它,并打印有效的填充颜色:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);
    const shape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 10, 10, 100, 100);
    shape.getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    shape.getFillFormat().getSolidFillColor().setSchemeColor(aspose.slides.SchemeColor.Accent4);
    presentation.getMasterTheme().getColorScheme().getAccent4().setColor(java.getStaticFieldValue("java.awt.Color", "RED"));
    presentation.save("theme-color.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

const savedPresentation = new aspose.slides.Presentation("theme-color.pptx");
try {
    const savedSlide = savedPresentation.getSlides().get_Item(0);
    const savedShape = savedSlide.getShapes().get_Item(0);
    const effectiveFill = savedShape.getFillFormat().getEffective();
    console.log("Effective fill color: " + effectiveFill.getSolidFillColor());
} finally {
    savedPresentation.dispose();
}

因为矩形仍然链接到 Accent4,主题更改后其可见颜色会变为红色。如果你在形状上用直接颜色替换了方案颜色,后续对 Accent4 的更改将不再影响该填充。

使用附加调色板中的颜色

PowerPoint 通过应用颜色转换来从主题颜色生成更亮和更暗的变体。Aspose.Slides 通过 ColorTransformOperation 枚举公开这些转换。

主题主色及从附加调色板生成的浅色和深色

1 - 主题主色。
2 - 从主题主色生成的浅色和深色变体。

以下示例基于 Accent4 创建六个矩形,对其中五个应用亮度转换,并保存结果:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);

    const shape1 = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 10, 10, 50, 50);
    shape1.getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    shape1.getFillFormat().getSolidFillColor().setSchemeColor(aspose.slides.SchemeColor.Accent4);

    const shape2 = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 10, 70, 50, 50);
    shape2.getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    shape2.getFillFormat().getSolidFillColor().setSchemeColor(aspose.slides.SchemeColor.Accent4);
    shape2.getFillFormat().getSolidFillColor().getColorTransform().add(aspose.slides.ColorTransformOperation.MultiplyLuminance, java.newFloat(0.2));
    shape2.getFillFormat().getSolidFillColor().getColorTransform().add(aspose.slides.ColorTransformOperation.AddLuminance, java.newFloat(0.8));

    const shape3 = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 10, 130, 50, 50);
    shape3.getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    shape3.getFillFormat().getSolidFillColor().setSchemeColor(aspose.slides.SchemeColor.Accent4);
    shape3.getFillFormat().getSolidFillColor().getColorTransform().add(aspose.slides.ColorTransformOperation.MultiplyLuminance, java.newFloat(0.4));
    shape3.getFillFormat().getSolidFillColor().getColorTransform().add(aspose.slides.ColorTransformOperation.AddLuminance, java.newFloat(0.6));

    const shape4 = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 10, 190, 50, 50);
    shape4.getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    shape4.getFillFormat().getSolidFillColor().setSchemeColor(aspose.slides.SchemeColor.Accent4);
    shape4.getFillFormat().getSolidFillColor().getColorTransform().add(aspose.slides.ColorTransformOperation.MultiplyLuminance, java.newFloat(0.6));
    shape4.getFillFormat().getSolidFillColor().getColorTransform().add(aspose.slides.ColorTransformOperation.AddLuminance, java.newFloat(0.4));

    const shape5 = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 10, 250, 50, 50);
    shape5.getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    shape5.getFillFormat().getSolidFillColor().setSchemeColor(aspose.slides.SchemeColor.Accent4);
    shape5.getFillFormat().getSolidFillColor().getColorTransform().add(aspose.slides.ColorTransformOperation.MultiplyLuminance, java.newFloat(0.75));

    const shape6 = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 10, 310, 50, 50);
    shape6.getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    shape6.getFillFormat().getSolidFillColor().setSchemeColor(aspose.slides.SchemeColor.Accent4);
    shape6.getFillFormat().getSolidFillColor().getColorTransform().add(aspose.slides.ColorTransformOperation.MultiplyLuminance, java.newFloat(0.5));

    presentation.save("theme-color-palette.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

这些变体仍然基于主题颜色。如果随后 Accent4 更改,转换后的颜色将根据新的 Accent4 值重新计算。

将 SchemeColor 值映射到 ColorScheme 槽

SchemeColor 枚举使用 Text1、Background1、Text2 和 Background2,而 ColorScheme 将相同的主题槽公开为 Dark1、Light1、Dark2 和 Light2。映射是固定的:

  • Text1 = Dark1
  • Background1 = Light1
  • Text2 = Dark2
  • Background2 = Light2

这些是同一主题槽的别名;它们不是会在两种形式之间动态转换的值。

更改主题字体

主题字体方案包含标题的主字体集和正文的次字体集。FontScheme.getMajor 和 FontScheme.getMinor 方法公开这些集合。

PowerPoint 兼容的主题字体标识符可在文本格式化中使用:

  • +mn-lt - 正文字体拉丁文(次要拉丁字体)
  • +mj-lt - 标题字体拉丁文(主要拉丁字体)
  • +mn-ea - 正文字体亚洲文字(次要亚洲字体)
  • +mj-ea - 标题字体亚洲文字(主要亚洲字体)

以下示例创建一个使用主拉丁主题字体的标题和一个使用次拉丁主题字体的正文行,然后更改主题字体并保存结果:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);

    const heading = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 40, 40, 500, 60);
    heading.getTextFrame().setText("Theme heading");
    heading.getTextFrame().getParagraphs().get_Item(0).getPortions().get_Item(0).getPortionFormat().setLatinFont(new aspose.slides.FontData("+mj-lt"));

    const body = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 40, 120, 500, 60);
    body.getTextFrame().setText("Theme body text");
    body.getTextFrame().getParagraphs().get_Item(0).getPortions().get_Item(0).getPortionFormat().setLatinFont(new aspose.slides.FontData("+mn-lt"));

    presentation.getMasterTheme().getFontScheme().getMajor().setLatinFont(new aspose.slides.FontData("Aptos Display"));
    presentation.getMasterTheme().getFontScheme().getMinor().setLatinFont(new aspose.slides.FontData("Arial"));
    presentation.save("theme-fonts.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

标题遵循主字体,正文文本遵循次字体。使用显式字体名称而非主题标识符的文本在主题字体方案更改时不会自动切换。

主字体和次字体集合还可以包含针对特定书写系统(如西里尔文、阿拉伯文、日文、格鲁吉亚文和塔安娜文)的字体映射。要检查、添加、替换或删除这些映射,请参阅 Script-Specific Theme Fonts。

复制或应用主题

下面的工作流解决不同的主题相关问题。

将外部主题应用于母版所依赖的幻灯片

当你拥有 PowerPoint 主题文件(.thmx)并希望重新样式化依赖特定母版的每张幻灯片时,请使用 MasterSlide.applyExternalThemeToDependingSlides。从 Presentation.getMasters 集合中选择母版(该集合由 MasterSlideCollection 表示),并将主题文件路径传递给该方法。

该方法执行以下操作:

  1. 基于所选母版创建一个新的母版幻灯片。
  2. 将外部主题应用到新母版。
  3. 将新母版分配给之前依赖所选母版的所有幻灯片。
  4. 返回新创建的 MasterSlide。

以下示例将外部主题应用于依赖第一个母版的幻灯片并保存演示文稿:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");

const presentation = new aspose.slides.Presentation("presentation.pptx");
try {
    const selectedMaster = presentation.getMasters().get_Item(0);
    const themedMaster = selectedMaster.applyExternalThemeToDependingSlides("corporate-theme.thmx");

    console.log("Created master: " + themedMaster.getName());
    presentation.save("presentation-with-external-theme.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

无效、损坏或不受支持的主题可能导致 PptxReadException。请验证用户提供的路径,处理文件系统访问失败,并仅在主题成功应用后再保存演示文稿。

仅重新分配依赖所选母版的幻灯片。关联其他母版的幻灯片保留其现有母版和主题。主题感知的颜色、字体、填充、线条、背景和效果会根据外部主题解析。直接分配的颜色、字体、填充和其他显式格式可能保持不变。布局级和幻灯片级的覆盖也可能优先于从新母版继承的值。

主题可能引用运行时环境中不存在的字体。为获得一致的渲染和导出,请安装所需字体、通过 custom font sources 提供,或配置 font substitution。

这是一种直接的母版级工作流:该方法接受 .thmx 文件的路径,无需手动创建幻灯片级或布局级的主题覆盖。

在多母版演示文稿中应用不同的外部主题

当事先不知道相关母版时,可通过 Slide.getLayoutSlide 和 LayoutSlide.getMasterSlide 从代表性幻灯片获取母版。应用任何主题之前,请先保存原始母版的引用,因为每次调用都会在演示文稿中创建另一个母版。

以下示例使用来自两个章节的幻灯片定位它们的母版,并为每组应用不同的外部主题:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");

const presentation = new aspose.slides.Presentation("multi-master-presentation.pptx");
try {
    if (presentation.getSlides().size() < 5) {
        console.log("The presentation does not contain the expected representative slides.");
    } else {
        const firstGroupMaster = presentation.getSlides().get_Item(0).getLayoutSlide().getMasterSlide();
        const secondGroupMaster = presentation.getSlides().get_Item(4).getLayoutSlide().getMasterSlide();

        if (firstGroupMaster.getSlideId() === secondGroupMaster.getSlideId()) {
            console.log("The representative slides use the same master.");
        } else {
            const firstThemedMaster = firstGroupMaster.applyExternalThemeToDependingSlides("blue-theme.thmx");
            const secondThemedMaster = secondGroupMaster.applyExternalThemeToDependingSlides("green-theme.thmx");

            console.log("First themed master: " + firstThemedMaster.getName());
            console.log("Second themed master: " + secondThemedMaster.getName());
            presentation.save("multi-master-with-external-themes.pptx", aspose.slides.SaveFormat.Pptx);
        }
    }
} finally {
    presentation.dispose();
}

第一次调用仅影响依赖 firstGroupMaster 的幻灯片,第二次调用仅影响依赖 secondGroupMaster 的幻灯片。属于其他母版的幻灯片不会被重新样式化。

在移动幻灯片时保留源主题

如果希望将幻灯片移动到另一个演示文稿并保留其原始设计,请使用 MasterSlideCollection.addClone 将源母版克隆到目标演示文稿中,然后使用 SlideCollection.addClone 将幻灯片连同克隆的母版一起克隆。这样可将母版、其布局以及关联的主题一起携带。

const aspose = {};
aspose.slides = require("aspose.slides.via.java");

const source = new aspose.slides.Presentation("source-theme.pptx");
try {
    const target = new aspose.slides.Presentation("target.pptx");
    try {
        const sourceSlide = source.getSlides().get_Item(0);
        const clonedMaster = target.getMasters().addClone(sourceSlide.getLayoutSlide().getMasterSlide());
        target.getSlides().addClone(sourceSlide, clonedMaster, true);
        target.save("theme-preserved.pptx", aspose.slides.SaveFormat.Pptx);
    } finally {
        target.dispose();
    }
} finally {
    source.dispose();
}

当源幻灯片必须在目标中保持相同外观时,这是首选工作流。仅将内容克隆到不相关的目标母版上可能会更改受主题驱动的颜色、字体、背景和效果。

将主题值应用于现有幻灯片

如果目标幻灯片必须保持当前的母版和布局,请从源主题初始化幻灯片级覆盖。OverrideTheme.initColorSchemeFrom、OverrideTheme.initFontSchemeFrom 和 OverrideTheme.initFormatSchemeFrom 方法将三个主要主题组件复制到覆盖中。

const aspose = {};
aspose.slides = require("aspose.slides.via.java");

const source = new aspose.slides.Presentation("source-theme.pptx");
try {
    const target = new aspose.slides.Presentation("target.pptx");
    try {
        const sourceTheme = source.getMasterTheme();
        const targetSlide = target.getSlides().get_Item(0);
        const overrideTheme = targetSlide.getThemeManager().getOverrideTheme();
        overrideTheme.initColorSchemeFrom(sourceTheme.getColorScheme());
        overrideTheme.initFontSchemeFrom(sourceTheme.getFontScheme());
        overrideTheme.initFormatSchemeFrom(sourceTheme.getFormatScheme());
        target.save("theme-applied-to-slide.pptx", aspose.slides.SaveFormat.Pptx);
    } finally {
        target.dispose();
    }
} finally {
    source.dispose();
}

这会更改该幻灯片使用的主题,而不影响其他幻灯片继承的主题。要移除本地覆盖并恢复继承值,请调用 OverrideTheme.clear。

将主题覆盖应用于布局

布局级覆盖适用于使用该布局的幻灯片,除非特定幻灯片拥有自己的覆盖。相同的初始化方法可通过 LayoutSlideThemeManager 使用:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");

const source = new aspose.slides.Presentation("source-theme.pptx");
try {
    const target = new aspose.slides.Presentation("target.pptx");
    try {
        const sourceTheme = source.getMasterTheme();
        const targetSlide = target.getSlides().get_Item(0);
        const overrideTheme = targetSlide.getLayoutSlide().getThemeManager().getOverrideTheme();
        overrideTheme.initColorSchemeFrom(sourceTheme.getColorScheme());
        overrideTheme.initFontSchemeFrom(sourceTheme.getFontScheme());
        overrideTheme.initFormatSchemeFrom(sourceTheme.getFormatScheme());
        target.save("theme-applied-to-layout.pptx", aspose.slides.SaveFormat.Pptx);
    } finally {
        target.dispose();
    }
} finally {
    source.dispose();
}

当需要许多布局和幻灯片共享相同基础设计时,使用母版或演示文稿级主题;当某一布局族需要不同样式时使用布局覆盖;仅在真正的例外情况下才使用幻灯片覆盖。过多的幻灯片级覆盖会使后续全局主题更改难以预测。

更新主题背景样式

主题的背景填充存储在 FormatScheme.getBackgroundFillStyles 中。PowerPoint 在 UI 中可以呈现比此集合实际存储的填充定义更多的背景选项,因为 UI 可以将主题填充与主题颜色和其他样式引用组合使用。

PowerPoint 演示文稿主题的背景样式库

在使用背景样式之前,检查已存储的集合以及当前的 Background.getStyleIndex。0 的样式索引表示没有主题填充;正数值是主题背景样式引用。这与直接对 JavaScript 集合进行索引不同,后者的 0 表示第一项。不要假设每个演示文稿都有相同数量的背景填充样式。

以下示例报告可用的背景填充计数,将主题背景引用分配给第一个母版,并保存演示文稿:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");

const presentation = new aspose.slides.Presentation("input.pptx");
try {
    const backgroundStyles = presentation.getMasterTheme().getFormatScheme().getBackgroundFillStyles();
    console.log("Background fill styles: " + backgroundStyles.size());
    if (backgroundStyles.size() === 0) {
        throw new Error("The presentation theme does not contain background fill styles.");
    }

    const masterSlide = presentation.getMasters().get_Item(0);
    masterSlide.getBackground().setType(java.newByte(aspose.slides.BackgroundType.Themed));
    masterSlide.getBackground().setStyleIndex(1);
    presentation.save("theme-background.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

可见结果取决于母版引用的主题条目以及布局或幻灯片级的任何背景覆盖。如果幻灯片使用了自己的背景,仅更改母版背景可能不会影响该幻灯片。需要获取继承后最终背景时,请使用 Background.getEffective。

更新主题效果

主题格式方案包含通过 FormatScheme.getFillStyles、FormatScheme.getLineStyles、FormatScheme.getEffectStyles 公开的独立填充、线条和效果样式集合。典型的 Office 主题通常包含三个主要样式条目,分别对应细微、适中和强烈的可视格式,但代码应检查每个集合,而不是假设固定数量。

对同一形状应用的细微、适中和强烈主题效果

在 JavaScript 中访问这些集合时,集合索引是零基的:索引 0 是第一条存储的样式,索引 2 是第三条。形状的样式引用索引是另一概念,由 ShapeStyle 暴露。修改主题样式会影响引用该主题样式的形状;直接格式化的形状可能保持不变。

以下示例检查所需的样式条目是否存在,更改第一条线条样式,修改第三条填充样式,在第三条效果样式中启用外阴影,并保存结果:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");

const presentation = new aspose.slides.Presentation("Subtle_Moderate_Intense.pptx");
try {
    const formatScheme = presentation.getMasterTheme().getFormatScheme();
    if (formatScheme.getLineStyles().size() < 1 || formatScheme.getFillStyles().size() < 3 || formatScheme.getEffectStyles().size() < 3) {
        throw new Error("The theme does not contain the style entries required by this example.");
    }

    formatScheme.getLineStyles().get_Item(0).getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    formatScheme.getLineStyles().get_Item(0).getFillFormat().getSolidFillColor().setColor(java.getStaticFieldValue("java.awt.Color", "RED"));
    formatScheme.getFillStyles().get_Item(2).setFillType(java.newByte(aspose.slides.FillType.Solid));
    formatScheme.getFillStyles().get_Item(2).getSolidFillColor().setColor(java.newInstanceSync("java.awt.Color", 34, 139, 34));
    const effectFormat = formatScheme.getEffectStyles().get_Item(2).getEffectFormat();
    effectFormat.enableOuterShadowEffect();
    effectFormat.getOuterShadowEffect().setDistance(10);
    presentation.save("theme-effects.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

对引用这些槽位的形状而言,第一条主题线条样式变为红色,第三条主题填充样式变为实心森林绿,第三条效果样式获得了距离为 10 点的外阴影。确切的视觉结果仍取决于每个形状引用的样式槽位以及是否有直接格式覆盖主题。

更改线条、填充和阴影设置后主题效果样式

确定有效实心填充是否使用主题颜色

填充可以直接存储在对象上,也可以从段落、布局、母版、主题样式或其他格式层级继承。调用 FillFormat.getEffective 可将该层级解析为不可变的有效填充快照。首先检查其 getFillType 值。仅当它是 FillType.Solid 时才读取实心填充属性。

对于实心填充,getSolidFillColor 返回在继承、主题查找和颜色转换应用后的最终渲染 RGB 值。getSolidFillSchemeColor 方法返回相应的逻辑 SchemeColor 槽,例如 Text1 或 Accent6。SchemeColor.NotDefined 表示有效实心填充并非基于方案颜色。在填充要么是主题颜色要么是直接 RGB 颜色的工作流中,此值标识直接 RGB 填充。

不要仅使用本地的 ColorFormat.getSchemeColor 值来对填充进行分类。例如,文本片段可能没有本地定义的方案颜色,其本地值为 NotDefined,但其有效填充继承自主题颜色并解析为 Text1 或 Accent6。相反,getSolidFillSchemeColor 告诉你哪个逻辑主题槽产生了有效颜色,但并不说明该槽来自对象、段落、布局、母版还是其他层级。

以下示例加载演示文稿,审计形状填充和文本片段填充,打印每个最终 RGB 值及其关联的方案颜色,并标记不会跟随主题颜色更改的实心填充:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");

function toHexColor(color) {
    const red = color.getRed().toString(16).padStart(2, "0");
    const green = color.getGreen().toString(16).padStart(2, "0");
    const blue = color.getBlue().toString(16).padStart(2, "0");
    return `#${red}${green}${blue}`.toUpperCase();
}

function auditFill(objectName, localFill) {
    const effectiveFill = localFill.getEffective();

    if (effectiveFill.getFillType() !== aspose.slides.FillType.Solid) {
        console.log(objectName + ": fill type = " + effectiveFill.getFillType() + "; not a solid fill.");
        return;
    }

    const rgb = effectiveFill.getSolidFillColor();
    const effectiveSchemeColor = effectiveFill.getSolidFillSchemeColor();
    const localSchemeColor = localFill.getSolidFillColor().getSchemeColor();

    console.log(objectName + ": RGB = " + toHexColor(rgb));
    console.log(objectName + ": local scheme = " + localSchemeColor + ", effective scheme = " + effectiveSchemeColor);

    if (effectiveSchemeColor === aspose.slides.SchemeColor.NotDefined) {
        console.log(objectName + ": direct RGB or another non-scheme fill; audit as theme-independent.");
    } else {
        console.log(objectName + ": theme-dependent through " + effectiveSchemeColor + ".");
    }
}

const presentation = new aspose.slides.Presentation("input.pptx");
try {
    const slideCount = presentation.getSlides().size();
    for (let slideIndex = 0; slideIndex < slideCount; slideIndex++) {
        const slide = presentation.getSlides().get_Item(slideIndex);

        const shapeCount = slide.getShapes().size();
        for (let shapeIndex = 0; shapeIndex < shapeCount; shapeIndex++) {
            const shape = slide.getShapes().get_Item(shapeIndex);
            const shapeName = "Slide " + (slideIndex + 1) + ", shape " + (shapeIndex + 1);
            auditFill(shapeName, shape.getFillFormat());

            if (java.instanceOf(shape, "com.aspose.slides.IAutoShape")) {
                const paragraphCount = shape.getTextFrame().getParagraphs().getCount();
                for (let paragraphIndex = 0; paragraphIndex < paragraphCount; paragraphIndex++) {
                    const paragraph = shape.getTextFrame().getParagraphs().get_Item(paragraphIndex);

                    const portionCount = paragraph.getPortions().getCount();
                    for (let portionIndex = 0; portionIndex < portionCount; portionIndex++) {
                        const portion = paragraph.getPortions().get_Item(portionIndex);
                        const portionName = shapeName + ", paragraph " + (paragraphIndex + 1) + ", portion " + (portionIndex + 1);
                        auditFill(portionName, portion.getPortionFormat().getFillFormat());
                    }
                }
            }
        }
    }
} finally {
    presentation.dispose();
}

NotDefined 分支提供了一个审计列表,列出在主题颜色槽更改时不会响应的实心填充。需要在演示文稿遵循新品牌配色时检查这些对象。报告的 RGB 值仍显示当前外观,而方案值解释了该外观是否与主题相连。

有效格式对象是快照。更改演示文稿主题、主题覆盖或任何继承的格式后,需再次调用 getEffective 并读取新的有效填充对象后再进行比较或报告颜色。

读取有效主题值

原始主题对象告诉你在特定层级上定义了什么。有效值告诉你在继承和本地覆盖解析后,幻灯片或形状实际使用了什么。对于幻灯片,调用 BaseOverrideThemeManager.createThemeEffective。对于背景,使用 Background.getEffective,对于填充,使用 FillFormat.getEffective。

以下示例读取幻灯片的有效主题、背景和第一形状填充:

const aspose = {};
aspose.slides = require("aspose.slides.via.java");

const presentation = new aspose.slides.Presentation("input.pptx");
try {
    const slide = presentation.getSlides().get_Item(0);
    const effectiveTheme = slide.getThemeManager().createThemeEffective();
    const effectiveBackground = slide.getBackground().getEffective();
    console.log("Effective major Latin font: " + effectiveTheme.getFontScheme().getMajor().getLatinFont().getFontName());
    console.log("Effective minor Latin font: " + effectiveTheme.getFontScheme().getMinor().getLatinFont().getFontName());
    console.log("Effective background fill type: " + effectiveBackground.getFillFormat().getFillType());
    if (slide.getShapes().size() > 0) {
        const effectiveFill = slide.getShapes().get_Item(0).getFillFormat().getEffective();
        console.log("First shape effective fill type: " + effectiveFill.getFillType());
        if (effectiveFill.getFillType() === aspose.slides.FillType.Solid) {
            console.log("First shape effective fill color: " + effectiveFill.getSolidFillColor());
        }
    }
} finally {
    presentation.dispose();
}

将有效数据用于渲染诊断、验证和比较。如果仅检查 Presentation.getMasterTheme,可能会错过更改最终外观的母版、布局、幻灯片或形状覆盖。

常见问题

应用外部主题会影响演示文稿中的每张幻灯片吗?

不会。 MasterSlide.applyExternalThemeToDependingSlides 仅重新分配依赖所选母版的幻灯片。使用其他母版的幻灯片保留其现有主题。

我可以在不更改母版的情况下将主题应用于单张幻灯片吗?

可以。使用该幻灯片的 SlideThemeManager 并初始化其覆盖主题。更改仅局部作用于该幻灯片;其他幻灯片继续继承其现有主题。

将主题从一个演示文稿迁移到另一个演示文稿的最安全方式是什么?

在移动幻灯片并保留源外观时,使用 MasterSlideCollection.addClone 将源母版克隆到目标演示文稿,再使用 SlideCollection.addClone 将幻灯片连同该母版一起克隆。这样可保持母版、布局和主题一起迁移。

如何查看继承和覆盖后得到的有效值?

对于幻灯片或布局主题,使用 BaseOverrideThemeManager.createThemeEffective。对于格式对象(如背景和填充),使用相应的有效数据方法,例如 Background.getEffective 和 FillFormat.getEffective。这些 API 在继承和覆盖应用后返回解析后的值。