管理使用 Python via Java 的簡報形狀

概述

Aspose.Slides for Python via Java 以有序的 ShapeCollection 來表示投影片上的形狀。此集合同時是您查找與修改形狀的所在,也是它們堆疊順序的來源:索引 0 為最背面的形狀,而最後一個索引則為最前面的形狀。

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

識別與尋找形狀

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

  • Name 對於開發人員控制的範本很有用,也能在 PowerPoint 的「選取窗格」中輕鬆檢查。名稱可以編輯且不保證唯一,若程式碼依賴它們,請訂定命名慣例。
  • AlternativeText 在已提供可存取性說明或作者標記的情況下很實用。它對使用者可見,可能會本地化或為可存取性重新編寫,且不保證唯一。請勿將有意義的可存取性文字靜默用作資料庫鍵。
  • OfficeInteropShapeId 為唯讀識別子,在投影片內唯一,對應 PowerPoint interop 使用的形狀 ID。當與 PowerPoint 整合或在形狀生命週期內需要不含歧義的參照時使用。被複製或重新建立的形狀是不同的形狀,會取得自己的 ID。

相關的 getUniqueId 方法會回傳簡報範圍的識別子,但此識別子僅供外掛使用,可能會重新指派,不應視為永久外部鍵。若身分認證必須長期保持,請在應用程式資料中保留對映,並驗證預期的形狀仍然存在。

以下範例以完全相同的比較方式依名稱搜尋,並回報投影片範圍的 interop ID。當範本未包含預期的形狀時,程式會回報該結果而非繼續使用錯誤的物件。

import jpype
import asposeslides

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

from asposeslides.api import Presentation

presentation = Presentation("input.pptx")
try:
    slide = presentation.getSlides().get_Item(0)

    target_shape = None
    for shape in slide.getShapes():
        if shape.getName() == "RevenueChart":
            target_shape = shape
            break

    if target_shape is None:
        print("The shape 'RevenueChart' was not found on slide 1.")
    else:
        print(f"Found {target_shape.getName()}; interop ID: {target_shape.getOfficeInteropShapeId()}")
finally:
    presentation.dispose()

當操作特定於形狀類型時,請先檢查類型再使用類型專屬的成員。此範例僅在命名物件為 AutoShape 時才更新文字與 alternative text。

import jpype
import asposeslides

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

from asposeslides.api import AutoShape, Presentation, SaveFormat

presentation = Presentation("input.pptx")
try:
    slide = presentation.getSlides().get_Item(0)

    candidate = None
    for shape in slide.getShapes():
        if shape.getName() == "StatusLabel":
            candidate = shape
            break

    if isinstance(candidate, AutoShape):
        candidate.getTextFrame().setText("Approved")
        candidate.setAlternativeText("Approval status: approved")
        presentation.save("identified-shape.pptx", SaveFormat.Pptx)
    else:
        print("'StatusLabel' is missing or is not an AutoShape.")
finally:
    presentation.dispose()

識別與修改預設形狀調整

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

不要只依賴固定的集合索引。請遍歷調整項目並檢查唯讀的 getType 方法,其回傳的 ShapeAdjustmentType 描述了調整控制的內容。唯讀的 getName 方法提供額外的辨識資訊,當一個預設包含多個相同語意類型的調整時尤其有用。

使用與調整意義相符的值方法:

調整類型 用途 變更的值方法
CornerSize 圓角大小 setRawValue
ArrowTailThickness 箭尾厚度 setRawValue
ArrowheadLength 箭頭長度 setRawValue
ArrowheadWidth 箭頭寬度 setRawValue
StartAngle 圓餅或弧線的起始角度 setAngleValue
EndAngle 圓餅或弧線的結束角度 setAngleValue

getTypegetName 僅回傳唯讀資訊。getRawValuesetRawValue 使用預設幾何單位的整數,而 getAngleValuesetAngleValue 使用度數。調整的數量、順序、意義與有效範圍取決於預設的 ShapeType。對於某一預設有效的值,對另一預設可能無效或產生不同效果。

getType 回傳 ShapeAdjustmentType.Custom 時,API 未識別標準語意。請檢查 getName、預設類型與現有值,除非已知其意義與範圍,否則保留調整不變。即使是已識別的類型,在選取值前也要確認同類型是否出現多次。Connector 文章說明了連接線彎曲調整的情況。

以下完整範例建立三個預設形狀的預設與修改版。它遍歷每個調整,回報名稱與類型,透過 setRawValue 變更尺寸相關值,透過 setAngleValue 變更角度,最後儲存結果。左欄保留預設幾何,右欄則顯示調整後的圓角矩形、四向箭頭與圓餅。

import jpype
import asposeslides

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

from asposeslides.api import Presentation, SaveFormat, ShapeAdjustmentType, ShapeType

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)

    # 為預設和已調整的形狀欄位加入標題。
    default_column_label = slide.getShapes().addAutoShape(ShapeType.Rectangle, 40, 20, 250, 30)
    default_column_label.getTextFrame().setText("Default preset geometry")
    adjusted_column_label = slide.getShapes().addAutoShape(ShapeType.Rectangle, 390, 20, 250, 30)
    adjusted_column_label.getTextFrame().setText("Modified adjustment values")

    slide.getShapes().addAutoShape(ShapeType.RoundCornerRectangle, 80, 70, 160, 70)
    modified_rounded_rectangle = slide.getShapes().addAutoShape(ShapeType.RoundCornerRectangle, 430, 70, 160, 70)
    modified_rounded_rectangle.setName("ModifiedRoundedRectangle")

    slide.getShapes().addAutoShape(ShapeType.QuadArrow, 80, 180, 160, 110)
    modified_arrow = slide.getShapes().addAutoShape(ShapeType.QuadArrow, 430, 180, 160, 110)
    modified_arrow.setName("ModifiedQuadArrow")

    slide.getShapes().addAutoShape(ShapeType.Pie, 95, 330, 130, 130)
    modified_pie = slide.getShapes().addAutoShape(ShapeType.Pie, 445, 330, 130, 130)
    modified_pie.setName("ModifiedPie")

    shapes_to_adjust = [modified_rounded_rectangle, modified_arrow, modified_pie]

    for shape in shapes_to_adjust:
        for adjustment_index in range(shape.getAdjustments().size()):
            adjustment = shape.getAdjustments().get_Item(adjustment_index)
            print(f"{shape.getName()} / {adjustment.getName()}: {adjustment.getType()}")

            if adjustment.getType() == ShapeAdjustmentType.CornerSize:
                adjustment.setRawValue(5000)
            elif adjustment.getType() == ShapeAdjustmentType.ArrowTailThickness:
                adjustment.setRawValue(25000)
            elif adjustment.getType() == ShapeAdjustmentType.ArrowheadLength:
                adjustment.setRawValue(30000)
            elif adjustment.getType() == ShapeAdjustmentType.ArrowheadWidth:
                adjustment.setRawValue(40000)
            elif adjustment.getType() == ShapeAdjustmentType.StartAngle:
                adjustment.setAngleValue(30)
            elif adjustment.getType() == ShapeAdjustmentType.EndAngle:
                adjustment.setAngleValue(300)
            elif adjustment.getType() == ShapeAdjustmentType.Custom:
                print(f"Custom adjustment '{adjustment.getName()}' was not changed.")

    presentation.save("preset-shape-adjustments.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

在變更值之前先檢查語意類型,可使程式碼明確表達意圖,避免假設相同集合索引在不同預設形狀間具有相同意義。

修改形狀集合

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

複製形狀

addClone 會建立獨立的副本並附加到目標集合的尾端。insertClone 亦會建立副本,但會放置在指定的 Z‑Order 索引。接受座標的重載會在不改變尺寸的情況下移動副本;接受寬度與高度的重載則可以同時調整尺寸。

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

import jpype
import asposeslides

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

from asposeslides.api import AutoShape, Presentation, SaveFormat, ShapeType, SlideLayoutType

presentation = Presentation()
try:
    source_slide = presentation.getSlides().get_Item(0)
    source_shape = source_slide.getShapes().addAutoShape(ShapeType.Rectangle, 40, 40, 180, 60)
    source_shape.setName("SourceLabel")
    source_shape.getTextFrame().setText("Source")

    blank_layout = presentation.getMasters().get_Item(0).getLayoutSlides().getByType(SlideLayoutType.Blank)
    destination_slide = presentation.getSlides().addEmptySlide(blank_layout)

    front_clone_shape = destination_slide.getShapes().addClone(source_shape, 80, 80)
    front_clone_shape.setName("FrontClone")
    if isinstance(front_clone_shape, AutoShape):
        front_clone_shape.getTextFrame().setText("Front clone")
    else:
        print("The front clone is not an AutoShape; its text was not changed.")

    back_clone_shape = destination_slide.getShapes().insertClone(0, source_shape, 80, 180)
    back_clone_shape.setName("BackClone")
    if isinstance(back_clone_shape, AutoShape):
        back_clone_shape.getTextFrame().setText("Back clone")
    else:
        print("The back clone is not an AutoShape; its text was not changed.")

    presentation.save("cloned-shapes.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

複製會將形狀的內容與格式(含名稱與 alternative text)一起複製。若這些值必須唯一,請為副本指派新的邏輯識別子。複雜形狀使用的資源由簡報處理,但副本仍是集合中的新項目,擁有新的形狀身分。

移除形狀

remove 會從其所在集合中刪除特定形狀物件。於索引化迭代中移除多個相符項目時,請從集合末端向前遍歷,以確保剩餘索引仍然有效。

此範例移除每一個具有指定名稱的形狀。它在當前索引讀取形狀,而非固定的集合項目,且不會不必要地型別轉換。

import jpype
import asposeslides

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

from asposeslides.api import Presentation, SaveFormat, ShapeType

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)

    keep_shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 40, 40, 140, 60)
    keep_shape.setName("Keep")

    first_temporary_shape = slide.getShapes().addAutoShape(ShapeType.Ellipse, 220, 40, 80, 80)
    first_temporary_shape.setName("Temporary")

    second_temporary_shape = slide.getShapes().addAutoShape(ShapeType.Triangle, 340, 40, 100, 80)
    second_temporary_shape.setName("Temporary")

    for i in range(slide.getShapes().size() - 1, -1, -1):
        shape = slide.getShapes().get_Item(i)
        if shape.getName() == "Temporary":
            slide.getShapes().remove(shape)

    presentation.save("removed-shapes.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

移除後,形狀計數與後續形狀的索引會變動。對未受影響的形狀保持引用通常比保存的索引更可靠。也請考慮連接線、動畫等可能參考被移除物件的投影片特性;移除可見形狀可能改變的不僅是外觀。

隱藏形狀

Hidden 設為 True 會保留形狀於集合中,但在正常投影片放映時不會出現。其索引、格式與內容仍可由程式碼存取,因此隱藏適合用於可能稍後還原的可選元素。

import jpype
import asposeslides

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

from asposeslides.api import Presentation, SaveFormat, ShapeType

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)

    visible_shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 40, 40, 160, 60)
    visible_shape.setName("VisibleLabel")

    optional_shape = slide.getShapes().addAutoShape(ShapeType.Moon, 240, 40, 100, 100)
    optional_shape.setName("OptionalDecoration")

    for shape in slide.getShapes():
        if shape.getName() == "OptionalDecoration":
            shape.setHidden(True)

    presentation.save("hidden-shape.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

隱藏並非刪除或安全保護。使用者或程式仍能找回並取消隱藏,且它仍屬於簡報檔案的一部份。

變更 Z‑Order

重疊的形狀依集合順序繪製。reorder 會將既有形狀移動到目標索引,而不會產生副本。索引 0 為最背面,集合 size 減一為最前面。

import jpype
import asposeslides

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

from asposeslides.api import FillType, Presentation, SaveFormat, ShapeType
from java.awt import Color

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)

    blue_rectangle = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 220, 120)
    blue_rectangle.setName("BlueRectangle")
    blue_rectangle.getFillFormat().setFillType(FillType.Solid)
    blue_rectangle.getFillFormat().getSolidFillColor().setColor(Color.BLUE)

    orange_ellipse = slide.getShapes().addAutoShape(ShapeType.Ellipse, 180, 140, 220, 120)
    orange_ellipse.setName("OrangeEllipse")
    orange_ellipse.getFillFormat().setFillType(FillType.Solid)
    orange_ellipse.getFillFormat().getSolidFillColor().setColor(Color.ORANGE)

    slide.getShapes().reorder(slide.getShapes().size() - 1, blue_rectangle)
    presentation.save("reordered-shapes.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

矩形最先建立,最初位於橢圓之後。將其移至最後索引即會置於前面。請在加入或複製所有相關形狀後再最後確定 Z‑Order,因為這些操作會追加或插入新的集合項目,可能改變原本的堆疊順序。

檢查版面投影片上的形狀

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

以下範例讀取每個版面形狀的 FillFormatLineFormat,而不假設每個形狀都是 AutoShape

import jpype
import asposeslides

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

from asposeslides.api import Presentation

presentation = Presentation("input.pptx")
try:
    for layout_slide in presentation.getLayoutSlides():
        for shape in layout_slide.getShapes():
            fill_type = shape.getFillFormat().getFillType()
            line_width = shape.getLineFormat().getWidth()
            print(f"{layout_slide.getName()} / {shape.getName()}: fill={fill_type}, line width={line_width}")
finally:
    presentation.dispose()

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

將形狀匯出為 SVG

ShapewriteAsSvg 方法會將單一形狀的渲染內容寫入串流。結果只包含該形狀本身,不會包含整張投影片的背景或鄰近形狀。

import jpype
import asposeslides

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

from asposeslides.api import Presentation
from pathlib import Path
from java.io import ByteArrayOutputStream

presentation = Presentation("input.pptx")
try:
    slide = presentation.getSlides().get_Item(0)

    if slide.getShapes().size() == 0:
        print("Slide 1 does not contain a shape to export.")
    else:
        shape = slide.getShapes().get_Item(0)
        svg_stream = ByteArrayOutputStream()
        try:
            shape.writeAsSvg(svg_stream)
            svg_bytes = bytes(svg_stream.toByteArray())
            Path("shape.svg").write_bytes(svg_bytes)
        except OSError as exception:
            print(f"The SVG file could not be written: {exception}")
        finally:
            svg_stream.close()
finally:
    presentation.dispose()

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

對齊形狀

SlideUtil.alignShapes 的多載可對齊全部形狀或指定的集合索引。ShapesAlignmentType 定義對齊的邊緣、中心線或分佈模式。將 align_to_slide 設為 True 即使用投影片邊緣;設為 False 則以選取的形狀相互對齊。

此範例將三個形狀對齊至投影片的上緣。對齊前會立即將返回的形狀參照轉換為其目前的索引。

import jpype
import asposeslides

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

from asposeslides.api import Presentation, SaveFormat, ShapeType, ShapesAlignmentType, SlideUtil

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)

    first_shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 60, 80, 120, 50)
    second_shape = slide.getShapes().addAutoShape(ShapeType.Ellipse, 240, 160, 120, 50)
    third_shape = slide.getShapes().addAutoShape(ShapeType.Triangle, 420, 240, 120, 50)
    first_shape.setName("FirstAlignedShape")
    second_shape.setName("SecondAlignedShape")
    third_shape.setName("ThirdAlignedShape")

    shape_indexes = jpype.JArray(jpype.JInt)([slide.getShapes().indexOf(first_shape), slide.getShapes().indexOf(second_shape), slide.getShapes().indexOf(third_shape)])

    SlideUtil.alignShapes(ShapesAlignmentType.AlignTop, True, slide, shape_indexes)
    presentation.save("aligned-shapes.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

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

翻轉形狀

ShapeFrame 類別儲存位置、大小、水平與垂直翻轉設定以及旋轉。其 getFlipHgetFlipV 使用 NullableBoolTrue 為啟用翻轉,False 為停用,NotDefined 為保留未指定/預設狀態。

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

翻轉前的形狀

此範例保留其他所有框架值,只更改兩個翻轉設定。這點很重要,因為指派新的 Frame 會取代完整的框架。

import jpype
import asposeslides

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

from asposeslides.api import NullableBool, Presentation, SaveFormat, ShapeFrame

presentation = Presentation("sample.pptx")
try:
    shape = presentation.getSlides().get_Item(0).getShapes().get_Item(0)
    frame = shape.getFrame()

    print(f"Horizontal flip before change: {frame.getFlipH()}")
    print(f"Vertical flip before change: {frame.getFlipV()}")

    flipped_frame = ShapeFrame(frame.getX(), frame.getY(), frame.getWidth(), frame.getHeight(), NullableBool.True_, NullableBool.True_, frame.getRotation())
    shape.setFrame(flipped_frame)

    presentation.save("flipped-shape.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

儲存後的形狀在水平方向與垂直方向皆被鏡像,同時保留位置、大小與旋轉。

翻轉後的形狀

常見問題

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

僅限於在集合不會變動且使用壽命極短的處理情境。對於有作者維護的範本,建議使用已驗證的 NameAlternativeText 慣例;對於需要投影片範圍 interop 的工作,則使用 OfficeInteropShapeId

隱藏形狀會從 Z‑Order 中移除嗎?

不會。隱藏的形狀仍保留在集合中且索引不變。它仍可被搜尋、重新排序、編輯或重新顯示。

為何複製的形狀會出現在另一個形狀的前面?

addClone 會將副本附加至集合的最後端,即 Z‑Order 的最前面。若想自行決定初始索引,可使用 insertClone,或在全部形狀加入後使用 reorder 調整。

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

僅在已驗證確切的預設與集合布局後才能這麼做。較佳的做法是遍歷 GeometryShape.getAdjustments 並檢查 AdjustValue.getType;若同一語意類型出現多次,請同時使用 AdjustValue.getName 作為額外資訊。