Google Slides APIを「画像生成ツール」として使い、note記事のヘッダー画像を自動生成する
はじめに
筆者はnoteで、現役クラウドインフラエンジニアの視点から財務分析・企業分析を読み解くシリーズ記事を書いています。以下の3カテゴリをローテーションで執筆しており、カテゴリごとに配色を変えたアイキャッチ画像(1280×670px)を毎回用意しています。
| カテゴリ | 内容 | カラー |
|---|---|---|
| 企業分析レポート | 決算資料などの公開情報をもとに1社を分析した記録 | ブルー系 |
| 財務諸表入門 | BS/PL/CFなどをエンジニアの言葉で読み解く解説 | グリーン系 |
| ツール活用ガイド | 財務分析に使えるツールの使い方をエンジニア向けに紹介 | オレンジ系 |
Canvaなどのデザインツールで毎回作ることもできますが、やることは「カテゴリに応じた背景色 + タイトルテキストの差し替え」だけ。それならAPIで自動化したほうがエンジニアらしいだろう、ということで Google Slides APIを「プログラマブルな画像生成ツール」として活用 し、CLIコマンド一発でヘッダー画像を生成するPythonスクリプトを作りました。
全体の仕組み
処理の流れはシンプルです。
- Google Slides APIでプレゼンテーションを作成(1280×670pxのカスタムサイズ)
- batchUpdateでスライド上に要素を配置(背景色、カテゴリバッジ、タイトル、著者名)
- getThumbnail APIでPNG画像としてエクスポート
- Drive APIで一時プレゼンテーションを削除
Google Slidesを「APIで操作できるキャンバス」として使い、完成したスライドをサムネイル画像として取り出す、という発想です。
前準備: GCPプロジェクトとOAuth認証
GCPコンソールでの設定
- Google Cloud Console でプロジェクトを作成
- 「APIとサービス」→「ライブラリ」から以下を有効化
- Google Slides API
- Google Drive API
- 「認証情報」→「OAuthクライアントID」を作成(種類は「デスクトップアプリ」)
- JSONファイルをダウンロードし、
tools/credentials/client_secret.jsonとして配置
必要なAPIスコープ
SCOPES = [
"https://www.googleapis.com/auth/presentations", # Slides API
"https://www.googleapis.com/auth/drive", # Drive API(削除用)
]
presentations スコープだけではプレゼンテーションの削除ができないため、drive スコープも必要です。
OAuth認証フロー
初回実行時にブラウザが開き、Googleアカウントでの認証が求められます。認証後は token.json が保存され、以降は自動認証されます。
def authenticate():
creds = None
if TOKEN_PATH.exists():
creds = Credentials.from_authorized_user_file(str(TOKEN_PATH), SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
str(CLIENT_SECRET_PATH), SCOPES
)
creds = flow.run_local_server(port=0)
TOKEN_PATH.write_text(creds.to_json())
return creds
run_local_server(port=0) は空きポートを自動選択してOAuthリダイレクトを受け取ります。
実装のポイント
EMU(English Metric Units)という独自単位
Google Slides APIで座標やサイズを指定する際、EMU という独自の単位を使います。これがこのAPI最大のハマりポイントです。
1インチ = 914,400 EMU
1ポイント = 12,700 EMU
noteのヘッダー画像は1280×670pxです。96dpiで換算すると以下のようになります。
# 1280px ÷ 96dpi × 914400 = 12,192,000 EMU
SLIDE_WIDTH_EMU = 12192000
# 670px ÷ 96dpi × 914400 = 6,381,750 EMU
SLIDE_HEIGHT_EMU = 6381750
このサイズでプレゼンテーションを作成することで、エクスポートした画像がそのままヘッダー画像になります。
def create_presentation(slides_service, title):
body = {
"title": title,
"pageSize": {
"width": {"magnitude": SLIDE_WIDTH_EMU, "unit": "EMU"},
"height": {"magnitude": SLIDE_HEIGHT_EMU, "unit": "EMU"},
},
}
presentation = slides_service.presentations().create(body=body).execute()
return presentation["presentationId"]
batchUpdateでスライド要素を一括配置
スライド上の要素はすべて batchUpdate でまとめて配置します。APIコールは1回で済みます。
スライドは以下の4層構造です。
| レイヤー | 要素 | 説明 |
|---|---|---|
| 1 | 背景矩形 | スライド全体を覆うベースカラー |
| 2 | オーバーレイ矩形 | 右半分に半透明の色を重ねてグラデーション風に |
| 3 | カテゴリバッジ | 角丸矩形+カテゴリ名テキスト |
| 4 | タイトル+著者名 | メインのテキスト要素 |
各要素は createShape → updateShapeProperties → insertText → updateTextStyle → updateParagraphStyle という流れで組み立てます。
例として、カテゴリバッジの角丸矩形の作成部分です。
{
"createShape": {
"objectId": "category_badge",
"shapeType": "ROUND_RECTANGLE",
"elementProperties": {
"pageObjectId": slide_id,
"size": {
"width": {"magnitude": 3200000, "unit": "EMU"},
"height": {"magnitude": 500000, "unit": "EMU"},
},
"transform": {
"scaleX": 1, "scaleY": 1,
"translateX": 800000, "translateY": 800000,
"unit": "EMU",
},
},
}
}
objectId は英数字・アンダースコア・ハイフンで自由に命名でき、後続のリクエストでこのIDを使ってスタイルやテキストを設定します。
カテゴリ別の配色
noteのシリーズごとに配色を辞書で定義し、コマンドライン引数で切り替えます。
CATEGORY_COLORS = {
"企業分析レポート": {
"gradient_start": {"red": 0.102, "green": 0.212, "blue": 0.365}, # ブルー系
"gradient_end": {"red": 0.169, "green": 0.424, "blue": 0.690},
},
"財務諸表入門": {
"gradient_start": {"red": 0.102, "green": 0.278, "blue": 0.192}, # グリーン系
"gradient_end": {"red": 0.184, "green": 0.522, "blue": 0.353},
},
"ツール活用ガイド": {
"gradient_start": {"red": 0.455, "green": 0.259, "blue": 0.063}, # オレンジ系
"gradient_end": {"red": 0.753, "green": 0.337, "blue": 0.129},
},
}
Google Slides APIの色指定は RGB各チャネル0〜1のfloat値 です。HEXカラーコードから変換するには 0x1A / 0xFF = 0.102 のように計算します。
グラデーションの擬似的な実現
shapeBackgroundFill は単色塗り(solidFill)のみ対応しており、CSSのような linear-gradient は直接使えません。そこで 右半分に半透明(alpha: 0.6)の矩形を重ねる ことでツートーンのグラデーション風にしています。
# 右半分にオーバーレイ
"size": {
"width": {"magnitude": SLIDE_WIDTH_EMU // 2, "unit": "EMU"},
"height": {"magnitude": SLIDE_HEIGHT_EMU, "unit": "EMU"},
},
"transform": {
"translateX": SLIDE_WIDTH_EMU // 2, # 右半分に配置
...
},
# alpha: 0.6 で半透明
"solidFill": {
"color": {"rgbColor": colors["gradient_end"]},
"alpha": 0.6,
}
getThumbnail APIの制約
画像のエクスポートには presentations.pages.getThumbnail を使います。
thumbnail = (
slides_service.presentations()
.pages()
.getThumbnail(
presentationId=presentation_id,
pageObjectId=slide_id,
thumbnailProperties_thumbnailSize="LARGE",
thumbnailProperties_mimeType="PNG",
)
.execute()
)
注意点として、thumbnailSize を LARGE に指定しても 最大幅は1600px です。今回は1280px幅なので問題ありませんが、それ以上の解像度が必要な用途には向きません。
返却値は画像データではなく一時的なURL(contentUrl)なので、別途HTTPリクエストでダウンロードします。
thumbnail_url = thumbnail["contentUrl"]
response = requests.get(thumbnail_url, timeout=30)
output.write_bytes(response.content)
一時プレゼンテーションのクリーンアップ
生成のたびにGoogleドライブにファイルが残ってしまうため、try/finally でDrive APIによる削除を保証しています。
finally:
if presentation_id:
try:
drive_service.files().delete(fileId=presentation_id).execute()
except Exception as e:
print(f"警告: プレゼンテーションの削除に失敗しました: {e}")
print(f"Google Drive から手動で削除してください (ID: {presentation_id})")
削除に失敗してもスクリプト全体は異常終了せず、手動削除を促すメッセージを出力します。
使い方
インストール
pip install google-api-python-client google-auth-oauthlib requests
実行例
python tools/generate_header.py \
--title "「売上」の正体を掘り下げてみた" \
--category "財務諸表入門" \
--output assets/header.png
カテゴリに応じて配色が自動で切り替わります。
まとめ
Google Slides APIは本来プレゼンテーション操作用のAPIですが、「スライド作成 → サムネイルエクスポート → 削除」というパターンで画像生成ツールとして活用できます。
EMUという独特な単位系には最初戸惑いますが、一度理解すればテキスト・図形・色を自由に組み合わせた画像をプログラムから生成できるようになります。noteに限らず、ブログやSNS向けのOGP画像生成などにも応用できるアプローチです。
Discussion