在 Python 中管理簡報圖形

概覽

Aspose.Slides for Python via .NET 將投影片上的圖形表示為有序的 ShapeCollection。集合同時是您尋找和修改圖形的場所,也是它們堆疊順序的來源:索引 0 為最背面的圖形,而最後的索引則為最前面的圖形。

本篇文章遵循此模型。它首先說明如何可靠地識別圖形並修改預設的圖形調整點,然後示範如何複製、移除、隱藏與重新排序圖形。最後的章節涵蓋版面層級的格式設定、SVG 匯出、對齊與翻轉設定。每個範例都是獨立的,您可以僅使用工作流程所需的操作。

識別與尋找圖形

在處理已知檔案時,集合索引很方便,但它們不是穩定的識別子。新增、移除或重新排序圖形都會改變其索引。請依照簡報的編寫與維護方式選擇識別子:

  • Shape.name 於開發人員控制的模板中很有用,且在 PowerPoint 的「選取窗格」中易於檢查。名稱可以編輯且不保證唯一,若程式碼依賴名稱,請建立命名慣例。
  • Shape.alternative_text 在可訪問性說明或作者自訂標籤已辨識圖形時有用。它對使用者可見,可能會本地化或為可訪問性重新撰寫,且不保證唯一。請勿將有意義的可訪問性文字默默用作資料庫金鑰。
  • Shape.office_interop_shape_id 為唯讀識別子,在投影片內唯一,對應 PowerPoint interop 使用的圖形 ID。於整合 PowerPoint 或需要在圖形生命週期內擁有明確參照時使用。被複製或重新建立的圖形是不同的圖形,會取得自己的 ID。

相關的 Shape.unique_id 屬性具有簡報範圍,但僅供外掛使用,且可能被重新指派。不要將它視為永久的外部鍵。若長期身份辨識很重要,請將映射保留在應用程式資料中,並驗證預期的圖形仍然存在。

以下範例以完全相等的方式使用 name 搜尋,並回報投影片範圍的 interop ID。當模板未包含預期的圖形時,程式會回報該結果而非繼續使用錯誤的物件。

import aspose.slides as slides

with slides.Presentation("input.pptx") as presentation:
    slide = presentation.slides[0]

    target_shape = None
    for shape in slide.shapes:
        if shape.name == "RevenueChart":
            target_shape = shape
            break

    if target_shape is None:
        print("The shape 'RevenueChart' was not found on slide 1.")
    else:
        print("Found {}; interop ID: {}".format(target_shape.name, target_shape.office_interop_shape_id))

當操作針對特定圖形類型時,請先檢查類型再使用該類型的成員。此範例僅在命名的物件為 AutoShape 時才更新文字與替代文字。

import aspose.slides as slides

with slides.Presentation("input.pptx") as presentation:
    slide = presentation.slides[0]

    candidate = None
    for shape in slide.shapes:
        if shape.name == "StatusLabel":
            candidate = shape
            break

    if isinstance(candidate, slides.AutoShape):
        candidate.text_frame.text = "Approved"
        candidate.alternative_text = "Approval status: approved"
        presentation.save("identified-shape.pptx", slides.export.SaveFormat.PPTX)
    else:
        print("'StatusLabel' is missing or is not an AutoShape.")

識別與修改預設圖形調整

預設幾何圖形可以暴露調整點,以控制角落大小、箭頭比例或弧度等特徵。請透過唯讀的 GeometryShape.adjustments 集合存取它們。集合本身由圖形提供,但每個 AdjustValue 含有可變更的值。

不要只依賴固定的集合索引。遍歷調整項目並檢查唯讀的 AdjustValue.type 屬性,其 ShapeAdjustmentType 值說明此調整控制什麼。唯讀的 AdjustValue.name 屬性提供額外辨識資訊,特別在同一預設包含多個相同語意類型的調整時很有用。

使用符合調整意義的值屬性:

調整類型 用途 要變更的值
CORNER_SIZE 圓角的大小 raw_value
ARROW_TAIL_THICKNESS 箭頭尾部的粗細 raw_value
ARROWHEAD_LENGTH 箭頭頭部的長度 raw_value
ARROWHEAD_WIDTH 箭頭頭部的寬度 raw_value
START_ANGLE 扇形或弧形的起始角度 angle_value
END_ANGLE 扇形或弧形的結束角度 angle_value

type 與 name 無法指派。raw_value 是預設幾何單位的可讀寫整數,而 angle_value 是度數的可讀寫角度。調整的數量、順序、意義與有效範圍取決於預設的 GeometryShape.shape_type。對於一個預設有效的值,對於另一個預設可能無效或產生不同效果。

當 type 為 ShapeAdjustmentType.CUSTOM 時,API 無法辨識標準語意。檢查 name、預設類型與現有值,除非已知預期意義與範圍,否則保持調整不變。即使是已辨識的類型,在選擇值前也要確認同一類型是否出現多次。Connector 文章示範了連接器彎曲調整的情況。

以下完整範例建立三個預設圖形的預設與修改版本。它遍歷每個調整,回報其 name 與 type,透過 raw_value 變更與大小相關的值,透過 angle_value 變更角度,並保存結果。左欄保留預設幾何,右欄顯示調整後的圓角矩形、四向箭頭與扇形。

import aspose.slides as slides

with slides.Presentation() as presentation:
    slide = presentation.slides[0]

    # 為預設與已調整的圖形欄位添加標題。
    default_column_label = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 40, 20, 250, 30)
    default_column_label.text_frame.text = "Default preset geometry"
    adjusted_column_label = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 390, 20, 250, 30)
    adjusted_column_label.text_frame.text = "Modified adjustment values"

    slide.shapes.add_auto_shape(slides.ShapeType.ROUND_CORNER_RECTANGLE, 80, 70, 160, 70)
    modified_rounded_rectangle = slide.shapes.add_auto_shape(slides.ShapeType.ROUND_CORNER_RECTANGLE, 430, 70, 160, 70)
    modified_rounded_rectangle.name = "ModifiedRoundedRectangle"

    slide.shapes.add_auto_shape(slides.ShapeType.QUAD_ARROW, 80, 180, 160, 110)
    modified_arrow = slide.shapes.add_auto_shape(slides.ShapeType.QUAD_ARROW, 430, 180, 160, 110)
    modified_arrow.name = "ModifiedQuadArrow"

    slide.shapes.add_auto_shape(slides.ShapeType.PIE, 95, 330, 130, 130)
    modified_pie = slide.shapes.add_auto_shape(slides.ShapeType.PIE, 445, 330, 130, 130)
    modified_pie.name = "ModifiedPie"

    shapes_to_adjust = [modified_rounded_rectangle, modified_arrow, modified_pie]

    for shape in shapes_to_adjust:
        for adjustment in shape.adjustments:
            print("{} / {}: {}".format(shape.name, adjustment.name, adjustment.type.name))

            if adjustment.type == slides.ShapeAdjustmentType.CORNER_SIZE:
                adjustment.raw_value = 5000
            elif adjustment.type == slides.ShapeAdjustmentType.ARROW_TAIL_THICKNESS:
                adjustment.raw_value = 25000
            elif adjustment.type == slides.ShapeAdjustmentType.ARROWHEAD_LENGTH:
                adjustment.raw_value = 30000
            elif adjustment.type == slides.ShapeAdjustmentType.ARROWHEAD_WIDTH:
                adjustment.raw_value = 40000
            elif adjustment.type == slides.ShapeAdjustmentType.START_ANGLE:
                adjustment.angle_value = 30
            elif adjustment.type == slides.ShapeAdjustmentType.END_ANGLE:
                adjustment.angle_value = 300
            elif adjustment.type == slides.ShapeAdjustmentType.CUSTOM:
                print("Custom adjustment '{}' was not changed.".format(adjustment.name))

    presentation.save("preset-shape-adjustments.pptx", slides.export.SaveFormat.PPTX)

在變更值之前檢查語意類型,使程式碼意圖清晰,並避免假設特定集合索引在不同預設圖形間具有相同意義。

修改圖形集合

新增、複製、移除與重新排序的方法會立即作用於集合。如果操作改變了圖形的數量或順序,請勿再依賴先前捕獲的索引。

複製圖形

ShapeCollection.add_clone 會建立獨立的副本並將其附加至目標集合的末端。ShapeCollection.insert_clone 也會建立副本,但會放置於指定的 Z 軸索引。接受座標的重載會在不變更大小的情況下移動副本;帶寬高的重載則可同時調整大小。

以下範例建立目的投影片,將標記矩形以 add_clone 複製到前方,並以 insert_clone 在背後插入第二個副本。對任一副本的變更都不會影響來源圖形。

import aspose.slides as slides

with slides.Presentation() as presentation:
    source_slide = presentation.slides[0]
    source_shape = source_slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 40, 40, 180, 60)
    source_shape.name = "SourceLabel"
    source_shape.text_frame.text = "Source"

    blank_layout = presentation.masters[0].layout_slides.get_by_type(slides.SlideLayoutType.BLANK)
    destination_slide = presentation.slides.add_empty_slide(blank_layout)

    front_clone_shape = destination_slide.shapes.add_clone(source_shape, 80, 80)
    front_clone_shape.name = "FrontClone"
    if isinstance(front_clone_shape, slides.AutoShape):
        front_clone_shape.text_frame.text = "Front clone"
    else:
        print("The front clone is not an AutoShape; its text was not changed.")

    back_clone_shape = destination_slide.shapes.insert_clone(0, source_shape, 80, 180)
    back_clone_shape.name = "BackClone"
    if isinstance(back_clone_shape, slides.AutoShape):
        back_clone_shape.text_frame.text = "Back clone"
    else:
        print("The back clone is not an AutoShape; its text was not changed.")

    presentation.save("cloned-shapes.pptx", slides.export.SaveFormat.PPTX)

複製會複製圖形的內容與格式,包括其名稱與替代文字。若這些值必須唯一,請為副本指派新的邏輯識別子。複雜圖形使用的資源由簡報處理,但副本仍是集合中新項目,具有新的圖形身份。

移除圖形

ShapeCollection.remove 會從其集合中刪除特定圖形物件。於索引迭代期間移除多個匹配項目時,請從結尾往前遍歷,以確保每個剩餘索引仍有效。

此範例移除所有具有指定名稱的圖形。它讀取 slide.shapes[index],而非固定的集合項目,且未不必要地強制轉型。

import aspose.slides as slides

with slides.Presentation() as presentation:
    slide = presentation.slides[0]

    keep_shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 40, 40, 140, 60)
    keep_shape.name = "Keep"

    first_temporary_shape = slide.shapes.add_auto_shape(slides.ShapeType.ELLIPSE, 220, 40, 80, 80)
    first_temporary_shape.name = "Temporary"

    second_temporary_shape = slide.shapes.add_auto_shape(slides.ShapeType.TRIANGLE, 340, 40, 100, 80)
    second_temporary_shape.name = "Temporary"

    for index in range(len(slide.shapes) - 1, -1, -1):
        shape = slide.shapes[index]
        if shape.name == "Temporary":
            slide.shapes.remove(shape)

    presentation.save("removed-shapes.pptx", slides.export.SaveFormat.PPTX)

移除後,圖形計數與之後圖形的索引皆會變更。對未受影響的圖形的引用比儲存的索引更可靠。也請考慮連接器、動畫與其他可能參照被移除物件的簡報功能;移除可見圖形可能會改變投影片外觀之外的更多內容。

隱藏圖形

將 Shape.hidden 設為 True 會保留圖形於集合中,但阻止其在普通投影片放映中出現。其索引、格式與內容仍可被程式碼存取,因而適合用於日後可能恢復的可選元素。

import aspose.slides as slides

with slides.Presentation() as presentation:
    slide = presentation.slides[0]

    visible_shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 40, 40, 160, 60)
    visible_shape.name = "VisibleLabel"

    optional_shape = slide.shapes.add_auto_shape(slides.ShapeType.MOON, 240, 40, 100, 100)
    optional_shape.name = "OptionalDecoration"

    for shape in slide.shapes:
        if shape.name == "OptionalDecoration":
            shape.hidden = True

    presentation.save("hidden-shape.pptx", slides.export.SaveFormat.PPTX)

隱藏並非刪除或安全措施。使用者或程式碼仍可發現並取消隱藏,它仍是簡報檔的一部份。

變更 Z 軸順序

重疊的圖形會依集合順序繪製。ShapeCollection.reorder 會將既有圖形移動至目標索引,而不會複製。索引 0 為最背,len(slide.shapes) - 1 為最前。

import aspose.pydrawing as draw
import aspose.slides as slides

with slides.Presentation() as presentation:
    slide = presentation.slides[0]

    blue_rectangle = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 100, 100, 220, 120)
    blue_rectangle.name = "BlueRectangle"
    blue_rectangle.fill_format.fill_type = slides.FillType.SOLID
    blue_rectangle.fill_format.solid_fill_color.color = draw.Color.steel_blue

    orange_ellipse = slide.shapes.add_auto_shape(slides.ShapeType.ELLIPSE, 180, 140, 220, 120)
    orange_ellipse.name = "OrangeEllipse"
    orange_ellipse.fill_format.fill_type = slides.FillType.SOLID
    orange_ellipse.fill_format.solid_fill_color.color = draw.Color.orange

    slide.shapes.reorder(len(slide.shapes) - 1, blue_rectangle)
    presentation.save("reordered-shapes.pptx", slides.export.SaveFormat.PPTX)

矩形最先建立,最初位於橢圓之後。將其移至最終索引即會置於前方。於新增或複製所有相關圖形後才最終確定 Z 軸順序,因為這些操作會附加或插入新集合項目,可能改變原先的堆疊。

檢查版面投影片上的圖形

普通投影片、版面投影片與母片投影片各自擁有獨立的圖形集合。版面集合中的圖形並非與普通投影片上相同位置的圖形同一個物件。當您需要了解或變更版面提供的格式時,請檢查版面圖形。

以下範例讀取每個版面圖形的 Shape.fill_format 與 Shape.line_format,且不假設每個圖形都是 AutoShape。

import aspose.slides as slides

with slides.Presentation("input.pptx") as presentation:
    for layout_slide in presentation.layout_slides:
        for shape in layout_slide.shapes:
            fill_type = shape.fill_format.fill_type
            line_width = shape.line_format.width
            print("{} / {}: fill={}, line width={}".format(layout_slide.name, shape.name, fill_type, line_width))

編輯版面可能會影響使用該版面的多張投影片。變更版面圖形之前,先確定普通投影片是繼承該物件還是有本機覆寫,並測試所有使用該版面的投影片。

將圖形匯出為 SVG

Shape.write_as_svg 會將單一圖形的渲染內容寫入串流。結果僅包含該圖形,而不包括整張投影片的背景或相鄰圖形。

import aspose.slides as slides

with slides.Presentation("input.pptx") as presentation:
    slide = presentation.slides[0]

    if len(slide.shapes) == 0:
        print("Slide 1 does not contain a shape to export.")
    else:
        shape = slide.shapes[0]
        with open("shape.svg", "wb") as svg_stream:
            shape.write_as_svg(svg_stream)

在渲染期間保持簡報開啟。輸出受圖形格式以及字型、影像等資源影響。若需要整個組合,請匯出投影片而非單一圖形。呼叫端負責擁有串流並在完成後關閉。

對齊圖形

SlideUtil.align_shapes 的多載可對齊全部圖形或選取的集合索引。ShapesAlignmentType 指定對齊的邊緣、中心線或分布模式。將 align_to_slide 設為 True 以使用投影片邊緣;設為 False 則相對於彼此對齊選取的圖形。

此範例將三個圖形對齊至投影片的上緣。它會在對齊前立即解析當前索引。

import aspose.slides as slides

with slides.Presentation() as presentation:
    slide = presentation.slides[0]

    first_shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 60, 80, 120, 50)
    second_shape = slide.shapes.add_auto_shape(slides.ShapeType.ELLIPSE, 240, 160, 120, 50)
    third_shape = slide.shapes.add_auto_shape(slides.ShapeType.TRIANGLE, 420, 240, 120, 50)
    first_shape.name = "FirstAlignedShape"
    second_shape.name = "SecondAlignedShape"
    third_shape.name = "ThirdAlignedShape"

    shape_indexes = [
        slide.shapes.index_of(first_shape),
        slide.shapes.index_of(second_shape),
        slide.shapes.index_of(third_shape)
    ]

    slides.util.SlideUtil.align_shapes(slides.ShapesAlignmentType.ALIGN_TOP, True, slide, shape_indexes)
    presentation.save("aligned-shapes.pptx", slides.export.SaveFormat.PPTX)

對齊會改變位置,而非 Z 軸順序。相對對齊通常需要至少兩個圖形,而水平或垂直分布則需要足夠的圖形以定義間距。若在呼叫方法前修改了集合,請重新計算索引。

翻轉圖形

ShapeFrame 類別儲存位置、大小、水平與垂直翻轉設定以及旋轉。其 flip_h 與 flip_v 值使用 NullableBool:TRUE 代表啟用翻轉,FALSE 代表停用,而 NOT_DEFINED 則保留未指定或預設狀態。

以下輸入簡報包含一個未翻轉的圖形。

翻轉前的圖形

此範例保留其他所有框架值,僅取代兩個翻轉設定。這很重要,因為指派新的 Shape.frame 會取代完整的框架。

import aspose.slides as slides

with slides.Presentation("sample.pptx") as presentation:
    shape = presentation.slides[0].shapes[0]
    frame = shape.frame

    print("Horizontal flip before change:", frame.flip_h)
    print("Vertical flip before change:", frame.flip_v)

    shape.frame = slides.ShapeFrame(
        frame.x, frame.y, frame.width, frame.height,
        slides.NullableBool.TRUE, slides.NullableBool.TRUE, frame.rotation)

    presentation.save("flipped-shape.pptx", slides.export.SaveFormat.PPTX)

儲存的圖形在水平與垂直方向皆為鏡像,同時保持其位置、大小與旋轉。

翻轉後的圖形

常見問題

我可以使用集合索引作為圖形識別子嗎?

僅在集合在使用索引前不會變更的短暫處理情境下可行。對於已編寫的模板,建議使用已驗證的 name 或 alternative_text 慣例,或在投影片範圍的 interop 工作中使用 office_interop_shape_id。

隱藏圖形會將其從 Z 軸順序移除嗎?

不會。隱藏的圖形仍保留於集合中,索引不變。它仍可被搜尋、重新排序、編輯,或再次顯示。

為什麼複製的圖形會出現在另一個圖形的前面?

add_clone 會將副本附加至集合的末端,即 Z 軸的最前端。若要選擇初始索引,可使用 insert_clone,或在全部圖形加入後使用 reorder。

我可以使用固定索引識別預設圖形調整嗎?

僅在已驗證確切預設與集合佈局後方可。建議遍歷 GeometryShape.adjustments 並檢查 AdjustValue.type;若同一語意類型出現多次,請使用 AdjustValue.name 作為額外資訊。