在 Python 中透過 Java 將簡報投影片渲染為 SVG 圖像

概述

SVG 是一種可擴展的基於 XML 的圖像格式,適用於網路發布、投影片檢視器、可及性工作流程以及自動後處理。Aspose.Slides 將每張投影片匯出為單獨的 SVG 檔案,並讓您控制文字、字型、圖片和 SVG 元素的寫入方式。

當匯出的 SVG 必須緊湊、在各瀏覽器間可預測或適合互動使用時,請使用 SVGOptions

將投影片匯出為 SVG

建立一個 Presentation,選取投影片,並使用 Slide.writeAsSvg 將其寫入串流。範例需要一個現有的 presentation.pptx 檔案。每個範例在需要時會啟動 JVM 並關閉其輸出串流。以下範例將簡報中的每張投影片匯出為單獨的 SVG 檔案。

import jpype
import asposeslides

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

from asposeslides.api import Presentation
from java.io import FileOutputStream

presentation = Presentation("presentation.pptx")
try:
    for slide in presentation.getSlides():
        output_file_name = f"slide-{slide.getSlideNumber()}.svg"
        svg_stream = FileOutputStream(output_file_name)
        try:
            slide.writeAsSvg(svg_stream)
        finally:
            svg_stream.close()
finally:
    presentation.dispose()

檔名使用 Slide.getSlideNumber 而非迴圈索引。當投影片檢視器或網頁只需要特定形狀時,亦可使用 Shape.writeAsSvg 匯出單一形狀。

配置 SVG 輸出

SVGOptions 控制 SVG 的呈現方式。對於文字框,SVGOptions.setUseFrameSize 會將文字框納入呈現區域,而 SVGOptions.setUseFrameRotation 則決定是否套用框的旋轉。當文字必須以不含連字的方式呈現時,將 SVGOptions.setDisableFontLigatures 設為 True

import jpype
import asposeslides

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

from asposeslides.api import Presentation, SVGOptions
from java.io import FileOutputStream

presentation = Presentation("presentation.pptx")
try:
    svg_options = SVGOptions()
    svg_options.setDisableFontLigatures(True)
    svg_options.setUseFrameSize(True)
    svg_options.setUseFrameRotation(False)

    slide = presentation.getSlides().get_Item(0)
    svg_stream = FileOutputStream("slide-with-custom-options.svg")
    try:
        slide.writeAsSvg(svg_stream, svg_options)
    finally:
        svg_stream.close()
finally:
    presentation.dispose()

控制文字與字型

向量化所有文字

SVGOptions.setVectorizeText 設為 True,即可將所有投影片文字寫成向量圖形。這可消除字型相依性,使視覺效果在各瀏覽器間更一致,但文字將不再可作為 SVG 文字被選取或搜尋。

import jpype
import asposeslides

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

from asposeslides.api import Presentation, SVGOptions
from java.io import FileOutputStream

presentation = Presentation("presentation.pptx")
try:
    svg_options = SVGOptions()
    svg_options.setVectorizeText(True)

    slide = presentation.getSlides().get_Item(0)
    svg_stream = FileOutputStream("slide-with-vectorized-text.svg")
    try:
        slide.writeAsSvg(svg_stream, svg_options)
    finally:
        svg_stream.close()
finally:
    presentation.dispose()

選擇外部字型的處理方式

SVGOptions.setExternalFontsHandling 會使用 SvgExternalFontsHandling 的值來處理外部載入的字型。選擇 AddLinksToFontFiles 以參考獨立的字型檔案,Embed 則將字型資料嵌入 SVG,或 Vectorize 將僅使用外部字型的文字渲染為圖形。嵌入字型前請確認字型授權。

import jpype
import asposeslides

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

from asposeslides.api import Presentation, SVGOptions, SvgExternalFontsHandling
from java.io import FileOutputStream

presentation = Presentation("presentation.pptx")
try:
    slide = presentation.getSlides().get_Item(0)
    font_modes = [
        ("slide-with-font-links.svg", SvgExternalFontsHandling.AddLinksToFontFiles),
        ("slide-with-embedded-fonts.svg", SvgExternalFontsHandling.Embed),
        ("slide-with-vectorized-external-fonts.svg", SvgExternalFontsHandling.Vectorize),
    ]
    for output_file_name, font_mode in font_modes:
        svg_options = SVGOptions()
        svg_options.setExternalFontsHandling(font_mode)
        svg_stream = FileOutputStream(output_file_name)
        try:
            slide.writeAsSvg(svg_stream, svg_options)
        finally:
            svg_stream.close()
finally:
    presentation.dispose()

縮小嵌入圖像尺寸

使用 SVGOptions.setPicturesCompression 降低嵌入圖片的解析度,利用 SVGOptions.setDeletePicturesCroppedAreas 省略裁剪過的來源區域,並透過 SVGOptions.setJpegQuality 控制 JPEG 編碼品質。這些設定會在減少檔案大小的同時,犧牲圖像保真度或保留的圖像資料。

import jpype
import asposeslides

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

from asposeslides.api import PicturesCompression, Presentation, SVGOptions
from java.io import FileOutputStream

presentation = Presentation("presentation.pptx")
try:
    svg_options = SVGOptions()
    svg_options.setPicturesCompression(PicturesCompression.Dpi150)
    svg_options.setDeletePicturesCroppedAreas(True)
    svg_options.setJpegQuality(80)

    slide = presentation.getSlides().get_Item(0)
    svg_stream = FileOutputStream("compressed-slide.svg")
    try:
        slide.writeAsSvg(svg_stream, svg_options)
    finally:
        svg_stream.close()
finally:
    presentation.dispose()

為形狀與文字指派穩定的 ID

使用透過 jpype.JProxy 註冊的 Python 格式化控制器,為形狀指派 SvgShape.setId 值,並為文字 tspan 元素指派 SvgTSpan.setId 值。透過 SVGOptions.setShapeFormattingController 指定此代理。

以下控制器使用 Shape.getOfficeInteropShapeId,該 ID 在形狀的生命週期內是穩定的,並使用可重複的計數器為其文字 span 產生 ID。這使得產生的 ID 適用於對未變更的簡報進行後處理。

import jpype
import asposeslides

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

from asposeslides.api import Presentation, SVGOptions
from java.io import FileOutputStream

class StableSvgIdController:
    def __init__(self):
        self.current_shape_id = ""
        self.text_span_index = 0

    def formatShape(self, svg_shape, shape):
        self.current_shape_id = f"shape-{shape.getOfficeInteropShapeId()}"
        self.text_span_index = 0
        svg_shape.setId(self.current_shape_id)

    def formatText(self, svg_tspan, portion, text_frame):
        svg_tspan.setId(f"{self.current_shape_id}-text-{self.text_span_index}")
        self.text_span_index += 1


presentation = Presentation("presentation.pptx")
try:
    controller = StableSvgIdController()
    proxy = jpype.JProxy("com.aspose.slides.ISvgShapeAndTextFormattingController", inst=controller)
    svg_options = SVGOptions()
    svg_options.setShapeFormattingController(proxy)

    slide = presentation.getSlides().get_Item(0)
    svg_stream = FileOutputStream("slide-with-stable-ids.svg")
    try:
        slide.writeAsSvg(svg_stream, svg_options)
    finally:
        svg_stream.close()
finally:
    presentation.dispose()

新增 SVG 事件處理程序

在 Python 格式化控制器中,使用帶有 SvgEvent 值的 SvgShape.setEventHandler 來為匯出的形狀加入 JavaScript 事件處理程序。透過 jpype.JProxy 註冊控制器,並以 SVGOptions.setShapeFormattingController 指定。於承載結果的頁面或 SVG 文件中定義 JavaScript 函式。

import jpype
import asposeslides

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

from asposeslides.api import Presentation, SVGOptions, SvgEvent
from java.io import FileOutputStream

class SvgEventController:
    def formatShape(self, svg_shape, shape):
        if shape.getName() == "ActionButton":
            svg_shape.setId("action-button")
            svg_shape.setEventHandler(SvgEvent.OnClick, "handleShapeClick(event)")


presentation = Presentation("presentation.pptx")
try:
    controller = SvgEventController()
    proxy = jpype.JProxy("com.aspose.slides.ISvgShapeFormattingController", inst=controller)
    svg_options = SVGOptions()
    svg_options.setShapeFormattingController(proxy)

    slide = presentation.getSlides().get_Item(0)
    svg_stream = FileOutputStream("interactive-slide.svg")
    try:
        slide.writeAsSvg(svg_stream, svg_options)
    finally:
        svg_stream.close()
finally:
    presentation.dispose()

主機頁面可定義事件處理程序所引用的 JavaScript 函式。指派 ID 與事件處理程序可支援投影片檢視器、可及性增強以及其他互動式 SVG 工作流程。

常見問題

何時應使用 SVGOptions.setVectorizeText 而非 SvgExternalFontsHandling.Vectorize

當所有文字必須與字型無關時,使用 SVGOptions.setVectorizeText。若僅需將使用外部字型的文字轉換為圖形,則使用 SvgExternalFontsHandling.Vectorize

如何使 SVG 檔案更小?

首先壓縮嵌入的圖片、刪除裁剪的圖像區域,並在目標環境能提供時選擇連結的字型檔案。必須測試結果,因為較低的圖像解析度、較低的 JPEG 品質以及向量化文字各有不同的品質與大小取捨。

匯出後我可以修改 SVG 元素嗎?

可以。透過格式化控制器指派 ID,然後在後處理工具或瀏覽器腳本中選取對應的 SVG 元素。