在 Python 中將 PowerPoint 簡報轉換為影片

概觀

將 PowerPoint 或 OpenDocument 簡報轉換為影片,讓觀眾可在影片播放器中觀看內容,而無需開啟簡報應用程式。Aspose.Slides for Python via Java 會將簡報的動畫與轉場渲染成影像幀。接著使用如 FFmpeg 等獨立編碼器,將這些幀合併為影片檔案。

將 PowerPoint 轉換為影片

轉換分為兩個階段:先以選定的影格速率產生 PNG 影格,然後將影像序列編碼為 MP4。兩個階段皆使用相同的影格速率,以保留動畫時序。

執行範例之前:

  1. 設定 Aspose.Slides for Python via Java
  2. 下載 FFmpeg 並確保其執行檔已加入 PATH。此範例使用內建 libx264 編碼器的版本。
  3. 在可寫入的目錄中執行以下 Python 程式碼。

此範例會建立一個帶有進入與退出動畫的笑臉形狀,於 30 FPS 產生影格,並呼叫 FFmpeg 產生 output.mp4。使用全新的影格目錄可避免將先前執行產生的影格納入影片。

import shutil
import subprocess
import tempfile
from pathlib import Path

import jpype
import asposeslides

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

from asposeslides.api import EffectPresetClassType, EffectSubtype, EffectTriggerType, EffectType, ImageFormat, Presentation, PresentationAnimationsGenerator, PresentationPlayer, ShapeType

fps = 30
frames_directory = Path(tempfile.mkdtemp(prefix="video_frames_", dir="."))
frame_count = 0

def save_frame(sender, arguments):
    global frame_count
    frame_path = frames_directory / f"frame_{frame_count:06d}.png"
    frame = arguments.getFrame()
    try:
        frame.save(str(frame_path), ImageFormat.Png)
    finally:
        frame.dispose()
    frame_count += 1

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)
    smile = slide.getShapes().addAutoShape(ShapeType.SmileyFace, 110, 20, 500, 500)
    sequence = slide.getTimeline().getMainSequence()
    entrance = sequence.addEffect(smile, EffectType.Fly, EffectSubtype.TopLeft, EffectTriggerType.AfterPrevious)
    entrance.getTiming().setDuration(2.0)
    exit_effect = sequence.addEffect(smile, EffectType.Fly, EffectSubtype.BottomRight, EffectTriggerType.AfterPrevious)
    exit_effect.setPresetClassType(EffectPresetClassType.Exit)
    exit_effect.getTiming().setDuration(2.0)

    generator = PresentationAnimationsGenerator(presentation)
    try:
        player = PresentationPlayer(generator, fps)
        try:
            callback = jpype.JProxy("com.aspose.slides.PresentationPlayer$FrameTick", dict(invoke=save_frame))
            player.setFrameTick(callback)
            generator.run(presentation.getSlides())
        finally:
            player.dispose()
    finally:
        generator.dispose()
finally:
    presentation.dispose()

ffmpeg = shutil.which("ffmpeg")
if frame_count == 0:
    print("No frames were generated.")
elif ffmpeg is None:
    print(f"FFmpeg was not found on PATH. PNG frames are available in {frames_directory}.")
else:
    input_pattern = str(frames_directory / "frame_%06d.png")
    command = [ffmpeg, "-n", "-framerate", str(fps), "-start_number", "0", "-i", input_pattern, "-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2", "-c:v", "libx264", "-pix_fmt", "yuv420p", "output.mp4"]
    result = subprocess.run(command, check=False)
    if result.returncode == 0:
        print("Saved output.mp4")
    else:
        print(f"FFmpeg failed with exit code {result.returncode}. Frames are available in {frames_directory}.")

若要轉換現有檔案,請以其路徑建立 Presentation,並省略建立形狀與動畫的程式碼。

FFmpeg 指令會讀取編號的影像序列,將奇數尺寸填充為偶數,並以 yuv420p 色彩格式寫入 H.264 影片。-n 參數可防止覆寫已存在的輸出檔。產生的 PNG 檔案會保留在影格目錄中;完成編碼後請自行移除。

影片效果

動畫控制投影片物件的出現、移動或消失方式。轉場則控制投影片之間的切換。請在產生影片影格之前加入這些效果。

請參閱 PowerPoint AnimationShape AnimationShape EffectsSlide Transitions

新增投影片轉場

以下獨立範例會建立一個包含兩張投影片的簡報。第二張投影片使用洋紅色背景與推入轉場。先儲存簡報,然後將其作為上述影格產生範例的輸入。

import jpype
import asposeslides

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

from asposeslides.api import BackgroundType, FillType, Presentation, SaveFormat, ShapeType, TransitionType

Color = jpype.JClass("java.awt.Color")

presentation = Presentation()
try:
    first_slide = presentation.getSlides().get_Item(0)
    first_slide.getShapes().addAutoShape(ShapeType.SmileyFace, 110, 20, 500, 500)
    new_slide = presentation.getSlides().addEmptySlide(first_slide.getLayoutSlide())
    new_slide.getBackground().setType(BackgroundType.OwnBackground)
    new_slide.getBackground().getFillFormat().setFillType(FillType.Solid)
    new_slide.getBackground().getFillFormat().getSolidFillColor().setColor(Color.MAGENTA)
    new_slide.getSlideShowTransition().setType(TransitionType.Push)
    presentation.save("transition.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

動畫段落

文字可以逐段落顯示。此範例建立三個段落,分別套用連續的淡入進場效果,且每個效果在前一個結束後延遲一秒。將已儲存的 paragraphs.pptx 檔案作為影片轉換範例的輸入。

import jpype
import asposeslides

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

from asposeslides.api import EffectSubtype, EffectTriggerType, EffectType, Paragraph, Portion, Presentation, SaveFormat, ShapeType

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)
    shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 210, 120, 300, 300)
    shape.addTextFrame("")
    paragraphs = shape.getTextFrame().getParagraphs()
    paragraphs.clear()
    sequence = slide.getTimeline().getMainSequence()
    texts = ["Aspose.Slides for Python via Java", "Convert presentation text to video", "Paragraph by paragraph"]

    for text in texts:
        paragraph = Paragraph()
        portion = Portion(text)
        paragraph.getPortions().add(portion)
        paragraphs.add(paragraph)
        effect = sequence.addEffect(paragraph, EffectType.Fade, EffectSubtype.None_, EffectTriggerType.AfterPrevious)
        effect.getTiming().setTriggerDelayTime(1.0)
        effect.getTiming().setDuration(1.0)

    presentation.save("paragraphs.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

影片轉換類別

PresentationAnimationsGenerator 會為投影片產生動畫事件。從簡報建立它時,會使用簡報的投影片尺寸作為影格大小。使用 setDefaultDelay 可設定預設的毫秒延遲。

PresentationPlayer 會以建構子提供的影格速率取樣產生的動畫。透過 JPype 使用 setFrameTick 註冊 Python 回呼,然後呼叫 run 產生影格。第一個範例使用自己的零基計數器,使檔名符合 FFmpeg 的輸入序列。

若需個別動畫狀態,可使用 setNewAnimation 註冊回呼。回呼會收到可定位於特定時間的動畫播放器。以下範例會以唯一的檔名儲存每個產生動畫的第一與最後影格:

from pathlib import Path

import jpype
import asposeslides

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

from asposeslides.api import EffectSubtype, EffectTriggerType, EffectType, ImageFormat, Presentation, PresentationAnimationsGenerator, ShapeType

output_directory = Path("animation_states")
output_directory.mkdir(exist_ok=True)
animation_index = 0

def save_animation_states(animation_player):
    global animation_index
    duration = animation_player.getDuration()
    print(f"Animation {animation_index}: {duration} milliseconds")
    for label, position in [("first", 0.0), ("last", duration)]:
        animation_player.setTimePosition(position)
        frame = animation_player.getFrame()
        try:
            frame_path = output_directory / f"animation_{animation_index:04d}_{label}.png"
            frame.save(str(frame_path), ImageFormat.Png)
        finally:
            frame.dispose()
    animation_index += 1

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)
    smile = slide.getShapes().addAutoShape(ShapeType.SmileyFace, 110, 20, 500, 500)
    sequence = slide.getTimeline().getMainSequence()
    effect = sequence.addEffect(smile, EffectType.Fly, EffectSubtype.TopLeft, EffectTriggerType.AfterPrevious)
    effect.getTiming().setDuration(2.0)

    generator = PresentationAnimationsGenerator(presentation)
    try:
        callback = jpype.JProxy("com.aspose.slides.PresentationAnimationsGenerator$NewAnimation", dict(invoke=save_animation_states))
        generator.setNewAnimation(callback)
        generator.run(presentation.getSlides())
    finally:
        generator.dispose()
finally:
    presentation.dispose()

支援的動畫與效果

下表彙總了 Java 轉換文章中描述的渲染支援情況。當簡報使用未受支援的效果時,請先預覽產生的影格。

進入:

Animation Type Aspose.Slides PowerPoint
Appear No Yes
Fade Yes Yes
Fly In Yes Yes
Float In Yes Yes
Split Yes Yes
Wipe Yes Yes
Shape Yes Yes
Wheel Yes Yes
Random Bars Yes Yes
Grow & Turn No Yes
Zoom Yes Yes
Swivel Yes Yes
Bounce Yes Yes

強調:

Animation Type Aspose.Slides PowerPoint
Pulse No Yes
Color Pulse No Yes
Teeter Yes Yes
Spin Yes Yes
Grow/Shrink No Yes
Desaturate No Yes
Darken No Yes
Lighten No Yes
Transparency No Yes
Object Color No Yes
Complementary Color No Yes
Line Color No Yes
Fill Color No Yes

退出:

Animation Type Aspose.Slides PowerPoint
Disappear No Yes
Fade Yes Yes
Fly Out Yes Yes
Float Out Yes Yes
Split Yes Yes
Wipe Yes Yes
Shape Yes Yes
Random Bars Yes Yes
Shrink & Turn No Yes
Zoom Yes Yes
Swivel Yes Yes
Bounce Yes Yes

移動路徑:

Animation Type Aspose.Slides PowerPoint
Lines Yes Yes
Arcs Yes Yes
Turns Yes Yes
Shapes Yes Yes
Loops Yes Yes
Custom Path Yes Yes

常見問題

Aspose.Slides 會直接產生 MP4 檔案嗎?

不會。Aspose.Slides 只產生簡報的影格,必須使用如 FFmpeg 之類的影片編碼器將它們合併為 MP4 檔案。

為何影片播放速度比預期快或慢?

請在影格產生與編碼器的輸入影格速率使用相同的 FPS。速率不一致會改變影像序列的播放時長。

我可以轉換受密碼保護的簡報嗎?

可以。於載入受保護的簡報 時提供正確的密碼,然後即可從已載入的內容產生影格。

此工作流程會保留簡報的音訊嗎?

範例僅匯出影像幀,產生的影片是無聲的。若需加入音訊,請在影片編碼時另行提供音軌。

如何減少暫存磁碟使用量?

使用較小的影格尺寸或較低的 FPS,並在成功編碼後移除暫存的 PNG 檔案。調整任一設定時,請檢查產生影片的品質。