こんにちは、広野です。
まとまった数の PowerPoint 資料 (PPTX ファイル) を PDF に変換したくて、AWS Lambda 関数をつくってみました。
記事を環境構築編と Lambda 関数編 (本記事) に分けて説明します。
やりたかったこと
多くの PowerPoint 資料 (PPTX) があり、それを RAG (Amazon Bedrock Knowledge Bases) に食わせたい。のですが、RAG が PPTX をソースデータファイルとしてサポートしておらず、一度 PDF に変換しないといけない事情がありました。簡単に変換できるよう、Amazon S3 バケットに置いたら自動変換してくれる処理をつくりました。PPTX – PDF 変換には LibreOffice を使用します。
- Amazon S3 バケットにファイルを置くと、イベント通知が発行されます。ファイルは input フォルダに置きます。
- Amazon EventBridge ルールで、input フォルダ内の .pptx ファイルであれば AWS Lambda 関数を呼び出します。
- Lambda 関数は、EventBridge から当該 PPTX ファイルのメタデータを受け取っているので、それをもとに PPTX ファイルを取得します。
- Lambda 関数内で、LibreOffice をヘッドレスで (No GUI で) 実行し、PPTX を PDF に変換します。
- 作成された PDF ファイルを Amazon S3 バケットの output フォルダに保存します。名前は元ファイル名の拡張子が .pdf に変わっただけのものです。
LibreOffice について
LibreOffice はオープンソースの Office ソフトウェアです。Word, Excel, PowerPoint などの Microsoft 製品と互換性があります。そのため、PowerPoint のファイルを扱うことができます。
この LibreOffice はヘッドレス、つまりコマンドで操作することができ、PowerPoint を PDF 変換する機能を利用します。
環境について
環境については、以下の記事をご覧ください。
環境をご理解いただいた上で、本記事の Lambda 関数の説明をお読みいただくことをお勧めします。
Lambda 関数について
Lambda 関数はコンテナ化するので、コンテナイメージの中に Python スクリプト (.py) が格納されています。LibreOffice 含む必要なモジュールがインストールされたコンテナイメージに。
必要なソースコードは以下の構成になっています。
- Dockerfile
ビルドフェーズで実行するコマンドが書かれています。主にコンテナイメージをビルドし、Amazon ECR に保存するのが目的です。 - buildspec.yml
ビルドフェーズでコンテナのベースイメージにモジュールをインストールしたり配置したりするコマンドが書かれています。 - lambda_function.py
Lambda 関数です。今回は Python で書かれており、Amazon EventBridge ルールから Amazon S3 オブジェクトのメタデータを受け取り、Amazon S3 へのファイル読み書きや LibreOffice の PDF 変換コマンドを実行します。 - cfn_container_lambda.yml
ビルドフェーズでデプロイされた Amazon ECR 内のコンテナイメージと、Lambda 関数を関連付けます。また、Amazon S3 バケットから発行されたイベント通知を受け取るための Amazon EventBridge ルールをデプロイします。
以下のように、AWS CodeCommit リポジトリにはこれらソースコードを特にフォルダー分けせず放り込んでいます。AWS CodeCommit リポジトリ内の main ブランチのソースコードが更新されると、環境構築編で構築した CI/CD パイプラインが動き出しコンテナ Lambda 関数が自動でデプロイされる仕組みです。
中のコードを紹介します。ところどころインラインでコメントします。
Dockerfile
FROM public.ecr.aws/shelf/lambda-libreoffice-base:25.8-python3.14-x86_64
COPY lambda_function.py ${LAMBDA_TASK_ROOT}
CMD [ "lambda_function.handler" ]
ものすごくシンプルです。
元々は自分で Amazon Linux 2023 や Python 用のベースイメージを使用していろいろインストールして動くものを作ったのですが、後から有志の方が LibreOffice 用のベースイメージを公開してくれていることに気付きました。ほんとよく出来ているので、それを使わせてもらっています。
この GitHub の中を覗くと、このベースイメージを作成するためのコマンドも書いてあり、もしフォントを追加したいなどあれば自分で加工したものを作れると思います。※フォント追加の必要性については後述します。
buildspec.yml
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Building the Docker image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- echo Pushing the Docker image...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
artifacts:
files:
- cfn_container_lambda.yml
環境変数多めです。どうしてもコマンド実行の際に環境特有の情報が必要になるので。それ以外は特別なことはしていません。
lambda_function.py
import json
import os
import subprocess
import boto3
s3 = boto3.client("s3")
WORKDIR = "/tmp"
def handler(event, context):
print("Event:", json.dumps(event))
# EventBridge - S3 情報取得
bucket = event["detail"]["bucket"]["name"]
key = event["detail"]["object"]["key"]
filename = os.path.basename(key)
local_pptx = f"{WORKDIR}/{filename}"
# S3 - /tmp にダウンロード
s3.download_file(bucket, key, local_pptx)
# LibreOffice変換
subprocess.run([
'libreoffice25.8',
'--headless',
'--invisible',
'--nodefault',
'--view',
'--nolockcheck',
'--nologo',
'--norestore',
'--convert-to',
'pdf',
'--outdir',
WORKDIR,
local_pptx
], check=True)
pdf_name = filename.replace(".pptx", ".pdf")
local_pdf = f"{WORKDIR}/{pdf_name}"
# 出力 S3 の output フォルダへ
output_key = f"output/{pdf_name}"
s3.upload_file(local_pdf, bucket, output_key)
return {
"status": "ok",
"input": key,
"output": output_key
}
こちらも特段特別なことはしていません。
Amazon EventBridge ルールから Amazon S3 の PPTX ファイルのメタデータを受け取り、それを元にファイルを取得。LibreOffice をヘッドレス実行して PDF 変換します。PDF を Amazon S3 バケットに戻す処理だけです。
cfn_container_lambda.yml
インラインでコメントします。Lambda 関数の箱の設定です。中身はコンテナイメージになるのでここには記述されません。
AWSTemplateFormatVersion: 2010-09-09
Description: The CloudFormation template that creates a Lambda function on container and a relevant IAM role.
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
# 以下のパラメータは自動で AWS CodePipeline から環境変数を受け取ります。
# デフォルト値は気にする必要はありませんが、定義を削除するとエラーになります。
Parameters:
SystemName:
Type: String
Description: System name. use lower case only. (e.g. example)
Default: example
MaxLength: 10
MinLength: 1
SubName:
Type: String
Description: System sub name. use lower case only. (e.g. prod or dev)
Default: dev
MaxLength: 10
MinLength: 1
ImageTag:
Type: String
Default: xxxxxxxxxxxxxxxxxxxx
MaxLength: 100
MinLength: 1
ImgRepoName:
Type: String
Default: xxxxxxxxxxxxxxxxxxxx
MaxLength: 100
MinLength: 1
S3BucketDocs:
Type: String
Default: xxxxxxxxxxxxxxxxxxxx
MaxLength: 200
MinLength: 1
Resources:
# ------------------------------------------------------------#
# Lambda
# ------------------------------------------------------------#
Lambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${SystemName}-${SubName}-pptx-pdf-conv
Description: !Sub Lambda Function to convert pptx to pdf for ${SystemName}-${SubName}
PackageType: Image
Timeout: 60
# メモリは 1024 MB にしました。670 MB ほど使用していましたので。512 MB だと処理に時間がかかりました。
# 1024 MB で、1 MB の PPTX の処理が 20 秒ほどかかりました。
# 10 MB を超える PPTX ファイルだと 1024 MB メモリをフルに消費し、時間も 60 秒タイムアウトを超過してしまいました。
# 取り扱うファイルサイズによってメモリサイズとタイムアウトは調整する必要があります。
MemorySize: 1024
EphemeralStorage:
Size: 512
Architectures:
- x86_64
# 環境変数として HOME を /tmp として設定しないと LibreOffice の実行が失敗します。
Environment:
Variables:
HOME: "/tmp"
Role: !GetAtt LambdaRole.Arn
Tags:
- Key: Cost
Value: !Sub ${SystemName}-${SubName}
# ここで、コンテナイメージを Lambda 関数にするように関連付けています。
Code:
ImageUri: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ImgRepoName}:${ImageTag}
DependsOn:
- LambdaRole
# ------------------------------------------------------------#
# Lambda Role (IAM)
# ------------------------------------------------------------#
LambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub LambdaRole-pptx-pdf-conv-${SystemName}-${SubName}
Description: This role allows Lambda functions to access S3 bucket.
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess
Policies:
- PolicyName: !Sub LambdaPolicy-pptx-pdf-conv-${SystemName}-${SubName}
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- "s3:PutObject"
- "s3:GetObject"
Resource:
- !Sub "arn:aws:s3:::${S3BucketDocs}/*"
# ------------------------------------------------------------#
# EventBridge Rule for starting Lambda function
# ------------------------------------------------------------#
EventBridgeRuleStartLambda:
Type: AWS::Events::Rule
Properties:
Name: !Sub ${SystemName}-${SubName}-pptx-pdf-conv-start-lambda
Description: !Sub This rule starts pptx pdf converter Lambda function for ${SystemName}-${SubName}. The trigger is the S3 event notifications.
EventBusName: !Sub "arn:aws:events:${AWS::Region}:${AWS::AccountId}:event-bus/default"
EventPattern:
source:
- "aws.s3"
detail-type:
- "Object Created"
detail:
bucket:
name:
- !Ref S3BucketDocs
object:
key:
- wildcard: "input/*.pptx"
State: ENABLED
Targets:
- Arn: !GetAtt Lambda.Arn
Id: !Sub ${SystemName}-${SubName}-pptx-pdf-conv-start-lambda
RoleArn: !GetAtt EventBridgeRuleLambdaRole.Arn
DependsOn:
- EventBridgeRuleLambdaRole
# ------------------------------------------------------------#
# EventBridge Rule Invoke Lambda Role (IAM)
# ------------------------------------------------------------#
EventBridgeRuleLambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub EventBridgeLambdaRole-${SystemName}-${SubName}
Description: !Sub This role allows EventBridge to invoke pptx pdf converter Lambda for ${SystemName}-${SubName}.
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: !Sub EventBridgeLambdaPolicy-${SystemName}-${SubName}
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- "lambda:InvokeFunction"
Resource:
- !GetAtt Lambda.Arn
DependsOn:
- Lambda
変換した PDF
結局、この方法で PPTX を PDF に変換するとどうなるのか。PPTX と PDF のスクリーンショットを撮って比較してみました。
コンテナイメージに Noto Sans CJK フォントが入っていたので、日本語変換は問題ありません。しかし元々使用していたフォントと異なるので、ところどころにレイアウト崩れが起きてしまいました。見た目を気にする資料だとフォントを合わせないと実用的ではなさそうです。フォント以外は特段問題なさそうだと感じました。
| PPTX | |
|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
まとめ
いかがでしたでしょうか。
本記事はコンテナ Lambda 関数の中身にフォーカスしていました。簡単でしたが LibreOffice の活用に触れられたと思います。アイデア次第で他の用途にも使えると思います。
本記事が皆様のお役に立てれば幸いです。









