管理使用 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

getType 與 getName 僅回傳唯讀資訊。getRawValue 與 setRawValue 使用預設幾何單位的整數,而 getAngleValue 與 setAngleValue 使用度數。調整的數量、順序、意義與有效範圍取決於預設的 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,因為這些操作會追加或插入新的集合項目,可能改變原本的堆疊順序。

檢查版面投影片上的形狀

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

以下範例讀取每個版面形狀的 FillFormat 與 LineFormat,而不假設每個形狀都是 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

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

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 類別儲存位置、大小、水平與垂直翻轉設定以及旋轉。其 getFlipH 與 getFlipV 使用 NullableBool:True 為啟用翻轉,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()

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

翻轉後的形狀

常見問題

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

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

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

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

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

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

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

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