在 Python 中通过 Java 处理演示文稿警告

概述

Aspose.Slides 在加载、渲染、转换或保存演示文稿时可以报告可恢复的问题。示例包括受损的源记录、无法保留的内容、字体替换以及目标格式的限制。警告回调允许应用程序记录这些情况并决定当前操作是否可以继续。

通过 jpype.JProxy 实现 IWarningCallback 接口,并检查通过 IWarningInfo 提供的 getWarningTypegetDescription 值。返回 ReturnAction.Continue 以接受警告,返回 ReturnAction.Abort 以停止操作。

在打开演示文稿时使用 LoadOptions.setWarningCallback 捕获警告。渲染和导出选项类继承自 SaveOptions.setWarningCallback,该回调接收来自幻灯片渲染、转换和保存的警告。由于警告本身并未标识具体的应用操作,建议在构建综合报告时将每个回调实例与操作阶段关联。

警告和异常

警告描述一种条件,Aspose.Slides 在回调返回 ReturnAction.Continue 时可以恢复。异常则表示请求的操作无法正常完成;异常不会转换为警告,也无法通过警告策略处理。

返回 ReturnAction.Abort 会让警告分发器通过抛出异常来终止当前操作。公开的异常类型取决于具体的操作和演示文稿格式。例如,加载时可能抛出 PptxReadExceptionPptReadException,而保存或导出时可能抛出 PptxException。请在操作边界捕获异常,并使用警告报告判断应用策略是否导致了终止,而不是仅依赖某一异常子类型或消息。回调在返回 ReturnAction.Abort 前会记录警告,确保原因对应用程序仍可用。

警告类别

WarningType 类提供以下类别的整数常量:

警告类型 含义 典型策略
SourceFileCorruption 源演示文稿包含腐败,可能导致以原始格式保存的文件不可用。 中止。
DataLoss 加载或保存后可能缺少文本、图表、图像或其他数据。 中止。
MajorFormattingLoss 演示文稿可能失去重要的格式。 在严格验证模式下中止;否则记录并继续。
MinorFormattingLoss 可能出现有限的格式差异。 记录用于诊断并继续。
CompatibilityIssue 结果可能无法在某些应用程序或旧版本中打开或正常工作。 记录日志并继续,除非兼容性是强制要求。
UnexpectedContent 源包含不受支持或未识别的内容,其影响可能尚不明确。 记录并继续,或在严格策略下视为错误。

类别应驱动策略决策。将 getDescription 返回的值存储用于诊断,但不要在应用逻辑中依赖其文字表述,因为不同警告场景和产品版本的消息文本可能会变化。

收集并分类警告

下面的示例为完整的处理管道使用一个应用级报告。单独的回调实例为加载、渲染、PDF 转换和 PPTX 保存标记警告。策略在源文件损坏或数据丢失时中止,可选在重大格式损失时中止,其他警告则继续。

import sys
from dataclasses import dataclass
from enum import Enum

import jpype
import asposeslides

if not jpype.isJVMStarted():
    jpype.startJVM()

from asposeslides.api import ImageFormat, LoadOptions, PdfOptions, PptxOptions, Presentation, RenderingOptions, ReturnAction, SaveFormat, WarningType


class OperationStage(Enum):
    Loading = "Loading"
    Rendering = "Rendering"
    Conversion = "Conversion"
    Saving = "Saving"


@dataclass(frozen=True)
class WarningEntry:
    stage: OperationStage
    type: int
    description: str


class WarningReport:
    def __init__(self):
        self._entries = []

    def get_entries(self):
        return tuple(self._entries)

    def add(self, stage, warning):
        entry = WarningEntry(stage, warning.getWarningType(), str(warning.getDescription()))
        self._entries.append(entry)


class WarningPolicy:
    def __init__(self, abort_on_major_formatting_loss):
        self.abort_on_major_formatting_loss = abort_on_major_formatting_loss

    def get_action(self, warning_type):
        if warning_type in (WarningType.SourceFileCorruption, WarningType.DataLoss):
            return ReturnAction.Abort
        if warning_type == WarningType.MajorFormattingLoss and self.abort_on_major_formatting_loss:
            return ReturnAction.Abort
        return ReturnAction.Continue


class ReportingWarningCallback:
    def __init__(self, stage, report, policy):
        self.stage = stage
        self.report = report
        self.policy = policy

    def warning(self, warning):
        self.report.add(self.stage, warning)
        return self.policy.get_action(warning.getWarningType())


def process_presentation(input_path, report, policy):
    try:
        load_options = LoadOptions()
        handler = ReportingWarningCallback(OperationStage.Loading, report, policy)
        callback = jpype.JProxy("com.aspose.slides.IWarningCallback", inst=handler)
        load_options.setWarningCallback(callback)
        presentation = Presentation(input_path, load_options)
        try:
            if not render_first_slide(presentation, report, policy):
                return False
            if not convert_to_pdf(presentation, report, policy):
                return False
            return save_validated_copy(presentation, report, policy)
        finally:
            presentation.dispose()
    except Exception as exception:
        print(f"Loading stopped: {exception}", file=sys.stderr)
        return False


def render_first_slide(presentation, report, policy):
    if presentation.getSlides().size() == 0:
        print("Rendering stopped: the presentation has no slides.", file=sys.stderr)
        return False
    try:
        options = RenderingOptions()
        handler = ReportingWarningCallback(OperationStage.Rendering, report, policy)
        callback = jpype.JProxy("com.aspose.slides.IWarningCallback", inst=handler)
        options.setWarningCallback(callback)
        image = presentation.getSlides().get_Item(0).getImage(options)
        try:
            image.save("slide-1.png", ImageFormat.Png)
            return True
        finally:
            image.dispose()
    except Exception as exception:
        print(f"Rendering stopped: {exception}", file=sys.stderr)
        return False


def convert_to_pdf(presentation, report, policy):
    try:
        options = PdfOptions()
        handler = ReportingWarningCallback(OperationStage.Conversion, report, policy)
        callback = jpype.JProxy("com.aspose.slides.IWarningCallback", inst=handler)
        options.setWarningCallback(callback)
        presentation.save("converted.pdf", SaveFormat.Pdf, options)
        return True
    except Exception as exception:
        print(f"Conversion stopped: {exception}", file=sys.stderr)
        return False


def save_validated_copy(presentation, report, policy):
    try:
        options = PptxOptions()
        handler = ReportingWarningCallback(OperationStage.Saving, report, policy)
        callback = jpype.JProxy("com.aspose.slides.IWarningCallback", inst=handler)
        options.setWarningCallback(callback)
        presentation.save("validated-output.pptx", SaveFormat.Pptx, options)
        return True
    except Exception as exception:
        print(f"Saving stopped: {exception}", file=sys.stderr)
        return False


def warning_type_name(warning_type):
    names = {
        WarningType.SourceFileCorruption: "SourceFileCorruption",
        WarningType.DataLoss: "DataLoss",
        WarningType.MajorFormattingLoss: "MajorFormattingLoss",
        WarningType.MinorFormattingLoss: "MinorFormattingLoss",
        WarningType.CompatibilityIssue: "CompatibilityIssue",
        WarningType.UnexpectedContent: "UnexpectedContent",
    }
    return names.get(warning_type, f"Unknown ({warning_type})")


report = WarningReport()
policy = WarningPolicy(True)
completed = process_presentation("input.pptx", report, policy)

print("Processing completed." if completed else "Processing stopped.")
for entry in report.get_entries():
    type_name = warning_type_name(entry.type)
    print(f"[{entry.stage.value}] {type_name}: {entry.description}")

在构造 WarningPolicy 时,如果可以接受重大格式差异,请将 abort_on_major_formatting_loss 设为 False。即使操作继续,兼容性问题、次要格式损失和意外内容仍会保留在报告中。若应用必须拒绝这些类别中的任意一种,可扩展 WarningPolicy.get_action 实现自定义逻辑。

常见警告场景

警告可能出现在工作流的不同阶段:

  • 数字签名:已签名的演示文稿在加载时可能触发警告,提示其签名将在处理过程中丢失。Aspose.Slides 通过 IPresentationSignedWarningInfo 报告此 DataLoss 情况。加载阶段的回调允许应用程序拒绝文件或明确接受报告的丢失。
  • 字体替换:在渲染或导出幻灯片时,无法使用的字体会被替换。字体替换警告被报告为 DataLoss,因此上述严格策略会中止,即使应用程序认为某个替换在视觉上是可接受的。要观察此行为,请使用包含运行时不可用字体的输入演示文稿。警告描述会标识替换的字体;请配置所需字体或 字体替换规则 后重新尝试。
  • 不受支持或意外内容:加载器可能遇到无法识别的演示文稿记录或特性。这类警告可能使用 UnexpectedContent,若已知数据或格式受到影响,则使用更严重的类别。
  • 格式兼容性:保存为其他演示文稿格式时可能省略某些特性,或导致结果在部分应用程序中表现不同。例如,将包含超过八条水平或垂直绘图参考线的演示文稿保存为旧版 PPT 会报告 CompatibilityIssue。保存阶段的回调可以记录此损失并继续,或在必须保留所有参考线时拒绝保存。
  • 加载行为:加载选项和旧版行为也可能产生警告。例如,IObsoletePresLockingBehaviorWarningInfo 将使用已废弃的演示文稿锁定行为标记为 CompatibilityIssue

警告取决于源文档、目标格式、操作以及 Aspose.Slides 版本。不要假设每个文件都会产生警告,也不要认为某种场景只能映射到唯一的类别。

安全处理已中止的操作

当回调返回 ReturnAction.Abort 时,不要使用加载失败的对象,也不要假设渲染或保存的输出已经完整。操作可能在创建输出文件后但在完成之前就已终止。

将验证后的结果保存到诸如 validated-output.pptx 的独立路径。仅在操作成功完成、警告报告符合应用策略且输出文件可打开检查后,才替换已有的演示文稿。这样可以避免用部分或被拒绝的结果覆盖有效的源文件。

空的警告报告并不保证所有源特性均已保留。请根据应用需求执行任何额外的内容和视觉检查。另请参阅 打开演示文稿保存演示文稿

常见问答

警告回调能处理 Aspose.Slides 的所有错误吗?

不能。它仅处理可恢复的、以警告形式报告的情况。那些独立于回调产生的异常必须在加载、渲染、转换或保存调用的外围由应用程序捕获。

返回 ReturnAction.Continue 能保证输出完全相同吗?

不能。它仅允许继续处理。报告的情况仍可能导致数据、格式或兼容性差异,请检查收集到的警告类型和描述。

应用程序如何识别产生警告的具体操作?

为每个操作创建一个回调实例,并将应用自定义的阶段信息与 getWarningTypegetDescription 返回的值一起存储,如示例所示。