在Python中从演示文稿获取形状的有效属性

了解本地、继承和有效属性

PowerPoint 的格式可以来源于多个位置。直接存储在对象上的值是其 本地值。如果该值未设置,PowerPoint 会查看父级格式来源,例如段落默认值、文本样式、布局或母版幻灯片、主题或演示文稿级别的默认值。这些值是 继承值。在整个层次结构解析完毕后剩余的值即为 有效值,用于渲染对象。

例如,文本片段可能没有自行定义字体高度。其本地 font_heightfloat("nan"),表示“此处未设置”。该片段可以从其段落、演示文稿的默认文本样式或其他适用来源继承高度。对片段格式调用 get_effective 会返回最终解析后的高度。

使用这两种格式数据用于不同的目的:

  • 在需要控制值定义位置时,读取或更改本地格式对象,例如 IPortionFormat
  • 在需要最终渲染结果时,读取有效数据对象,例如 IPortionFormatEffectiveData。有效数据为只读。

比较本地、继承和有效值

以下完整示例创建一个形状,并在演示文稿、段落和片段层级上分别应用字体高度。每一步都会打印这些层级定义的值以及同一文本片段的结果有效值。示例还演示了为何在格式更改后必须重新读取有效数据。

import math

import aspose.slides as slides


def format_local_value(value):
    return "<not set>" if math.isnan(value) else str(value)


def print_font_heights(caption, presentation, paragraph, portion):
    presentation_value = presentation.default_text_style.get_level(0).default_portion_format.font_height
    paragraph_value = paragraph.paragraph_format.default_portion_format.font_height
    local_value = portion.portion_format.font_height

    # 在先前的更改之后读取有效数据。
    effective_value = portion.portion_format.get_effective().font_height

    print(caption)
    print("  Presentation default: " + format_local_value(presentation_value))
    print("  Paragraph default:    " + format_local_value(paragraph_value))
    print("  Portion local:        " + format_local_value(local_value))
    print("  Portion effective:    " + str(effective_value))


with slides.Presentation() as presentation:
    slide = presentation.slides[0]
    shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 100, 100, 500, 80, False)
    text_frame = shape.add_text_frame("Effective formatting")
    paragraph = text_frame.paragraphs[0]
    portion = paragraph.portions[0]

    # 在两个不同层级上定义继承值。
    presentation.default_text_style.get_level(0).default_portion_format.font_height = 20
    paragraph.paragraph_format.default_portion_format.font_height = 28

    print_font_heights("The portion inherits from the paragraph", presentation, paragraph, portion)

    # 片段的本地值覆盖两个继承值。
    portion.portion_format.font_height = 36
    print_font_heights("A local value overrides inherited values", presentation, paragraph, portion)

    # 更改继承值不会覆盖已有的本地值。
    paragraph.paragraph_format.default_portion_format.font_height = 30
    print_font_heights("The local value still has priority", presentation, paragraph, portion)

    # 清除本地值。片段现在再次从段落继承。
    portion.portion_format.font_height = float("nan")
    print_font_heights("The local value is cleared", presentation, paragraph, portion)

    # 清除段落值。演示文稿默认值现在提供结果。
    paragraph.paragraph_format.default_portion_format.font_height = float("nan")
    print_font_heights("The paragraph value is cleared", presentation, paragraph, portion)

    presentation.save("effective-properties.pptx", slides.export.SaveFormat.PPTX)

本示例的优先级顺序为片段本地格式、段落格式、演示文稿默认。其他对象可能拥有不同的继承链,但原则相同:更具体的显式值获胜,且 get_effective 返回最终结果。

获取有效的文本属性

文本格式分布在多个对象中:

对于下一个示例,text-formatting.pptx 必须至少包含一张幻灯片和一个带有非空文本框的 AutoShape。AutoShape 可以位于形状集合中的任意位置;代码会搜索合适的对象并在使用前进行验证。

import aspose.slides as slides


def has_non_empty_text(shape):
    if not isinstance(shape, slides.AutoShape):
        return False
    if shape.text_frame is None:
        return False
    if shape.text_frame.paragraphs.count == 0:
        return False
    return shape.text_frame.paragraphs[0].portions.count > 0


with slides.Presentation("text-formatting.pptx") as presentation:
    if presentation.slides.count == 0:
        raise RuntimeError("The presentation contains no slides.")

    shape = None
    for candidate in presentation.slides[0].shapes:
        if has_non_empty_text(candidate):
            shape = candidate
            break

    if shape is None:
        raise RuntimeError("The first slide must contain an AutoShape with non-empty text.")

    text_frame = shape.text_frame
    paragraph = text_frame.paragraphs[0]
    portion = paragraph.portions[0]

    text_frame_effective = text_frame.text_frame_format.get_effective()
    paragraph_effective = paragraph.paragraph_format.get_effective()
    portion_effective = portion.portion_format.get_effective()

    print("Text frame margins:")
    print("  Left: " + str(text_frame_effective.margin_left))
    print("  Top: " + str(text_frame_effective.margin_top))
    print("  Right: " + str(text_frame_effective.margin_right))
    print("  Bottom: " + str(text_frame_effective.margin_bottom))
    print("Paragraph alignment: " + str(paragraph_effective.alignment))
    print("Font height: " + str(portion_effective.font_height))
    print("Bold: " + str(portion_effective.font_bold))

    effective_text_style = text_frame.text_frame_format.text_style.get_effective()
    for level in range(9):
        level_effective = effective_text_style.get_level(level)
        print("Level " + str(level) + " indent: " + str(level_effective.indent))

获取有效的 3D 属性

IThreeDFormat.get_effective() 返回一个 IThreeDFormatEffectiveData 对象,其中聚合了所有已解析的 3D 设置。其 cameralight_rigbevel_topbevel_bottom 属性公开相应的有效数据。一起读取这些相关设置可以更容易理解形状的最终 3D 外观。

对于此示例,shape-3d.pptx 必须在其第一页至少包含一个形状。如果希望输出包含除默认值之外的数值,请对该形状应用 3D 摄像机、光照或倒角设置。

import aspose.slides as slides


with slides.Presentation("shape-3d.pptx") as presentation:
    if presentation.slides.count == 0 or presentation.slides[0].shapes.count == 0:
        raise RuntimeError("The first slide must contain a shape.")

    shape = presentation.slides[0].shapes[0]
    three_d_effective = shape.three_d_format.get_effective()

    print("Camera:")
    print("  Type: " + str(three_d_effective.camera.camera_type))
    print("  Field of view: " + str(three_d_effective.camera.field_of_view_angle))
    print("  Zoom: " + str(three_d_effective.camera.zoom))

    print("Light rig:")
    print("  Type: " + str(three_d_effective.light_rig.light_type))
    print("  Direction: " + str(three_d_effective.light_rig.direction))

    print("Top bevel:")
    print("  Type: " + str(three_d_effective.bevel_top.bevel_type))
    print("  Width: " + str(three_d_effective.bevel_top.width))
    print("  Height: " + str(three_d_effective.bevel_top.height))

获取有效的表格格式

表格格式可以来自表格样式,也可以来自整体表格、列、行或单元格的格式。对于显式填充的冲突,优先级顺序为单元格、行、列,然后是整个表格。单元格的有效格式即用于绘制该单元格的最终格式。

对于此示例,table-formatting.pptx 必须在其第一页至少包含一个表格。该表格必须至少有一行和一列。代码会搜索一个 Table,而不是假设 shapes[0] 是表格。

import aspose.slides as slides


with slides.Presentation("table-formatting.pptx") as presentation:
    if presentation.slides.count == 0:
        raise RuntimeError("The presentation contains no slides.")

    table = None
    for shape in presentation.slides[0].shapes:
        if isinstance(shape, slides.Table):
            table = shape
            break

    if table is None:
        raise RuntimeError("The first slide must contain a table.")

    if table.rows.count == 0 or table.columns.count == 0:
        raise RuntimeError("The table must contain at least one cell.")

    table_effective = table.table_format.get_effective()
    row_effective = table.rows[0].row_format.get_effective()
    column_effective = table.columns[0].column_format.get_effective()
    cell_effective = table.rows[0][0].cell_format.get_effective()

    print("Table fill: " + str(table_effective.fill_format.fill_type))
    print("Row fill: " + str(row_effective.fill_format.fill_type))
    print("Column fill: " + str(column_effective.fill_format.fill_type))
    print("Final cell fill: " + str(cell_effective.fill_format.fill_type))

如果需要获取颜色而不仅仅是填充类型,首先检查有效的 fill_type,然后读取对应类型的属性,例如针对实体填充的 solid_fill_color

更改后重新读取有效数据

有效数据描述了解析时的格式层次结构。更改任何可能参与该层次结构的内容后,请再次调用 get_effective,包括:

  • 对象的本地格式;
  • 段落或文本框的默认值;
  • 表格样式、表格、列、行或单元格的格式;
  • 布局或母版幻灯片的格式;
  • 主题数据或演示文稿级别的默认值;
  • 分配给幻灯片的布局或母版。

不要将有效数据对象作为永久快照保存。Aspose.Slides 可能在内部缓存部分有效数据,后续的 get_effective 调用可以刷新这些数据。如果需要在更改前后比较数值,请在更改之前将所需的标量值(如字体高度、颜色、对齐方式或倒角宽度)复制到自己的变量中。

若要更改某个值,请更新相应的本地格式对象,然后调用 get_effective 验证结果。有效数据对象本身是只读的。

FAQ

如何判断是哪个层级提供了有效值?

有效数据只包含最终值,不指明其来源。需从最具体的层级向外检查相应的本地对象。对文本而言,这可能包括片段、段落、文本框、布局、母版、主题以及演示文稿默认值。float("nan")None 等未定义值表示搜索将继续到更高层级。

如果没有任何层级定义某属性会怎样?

Aspose.Slides 会解析出相应的 PowerPoint 或库默认值。该解析后的值会出现在有效数据中,即使没有本地对象显式定义它。

为什么有效值有时等于本地值?

本地值在继承计算中获胜。这在属性显式设置在对象上且没有更具体的规则覆盖时是预期行为。

何时应该使用本地数据而不是有效数据?

在需要检查或编辑特定格式层级时使用本地数据。需要在继承、主题规则和适用样式全部解析后得到的最终外观时使用有效数据。完整比较示例 在同一工作流中演示了两者的使用。