どうも。Kenny(tsujikenzo)です。 今日は単発で、 「GASでPDFを画像に変換しよう」 をお送りします。シリーズの続きみたいなものですね。
前回のおさらい
前回のシリーズでは、GASでPDFを分割・結合しました。
今回は、GASでPDFを画像に変換しよう、をお届けします。
画像とは
「Gemini APIで画像認識をしてみよう」のブログで、Geminiは、以下の画像形式をサポートしていることをお伝えしました。
- PNG – image/png
- JPEG – image/jpeg
- WEBP – image/webp
- HEIC – image/heic
- HEIF – image/heif
なので、PDFをいずれかの画像形成に変換しておけば、画像認識まで自動化できそうです。
GASによる画像化
しかしながらGASでは、以下の理由により、PDFを画像化できません。
- PDF→画像(JPG/PNG)変換するメソッドがない
- 前述したPDF処理用のライブラリ(pdf-lib)には、画像化のメソッド(関数含む)がない
- Pythonのpdf2imageには、convert_from_path() や convert_from_bytes()があって可能
- Google DriveのgetThumbnail()メソッドは低解像度のプレビュー画像(128 x 128 px)を取得する
- Google ドキュメントやスライドにPDFを挿入したとしても画像としてダウンロードはできない
わたし(凡人)の頭では、万策尽きまして、今回はPDFを画像に変換するサービスを使うことにしました。
PDFを画像に変換するサービス
みなさんも、インターネット上で、PDFを分割したり結合したり画像に変換するサービスを利用したことがあると思います。
老舗オンラインPDF処理サービスの1つI LOVE PDFなどは、有名ですね。
他にも、数多くのサービスがありますが、APIを公開しているサービスもあります。
この中で、わたしのような初学者でも扱えるものがないか、毎夜吐いたり泣いたりしながら、たどり着いたサービスが、PDF.coです。
1カ月は無料で使えて、大量の画像化処理をしてくれて月額1,500円なら投資することにしました。そのうちGeminiがPDFにも対応したらサブスク解除します。

今回は、こちらのAPIを使って、PDFを画像に変換してみましょう。
APIの公式リファレンスはこちらです。
下準備
いくつか、下準備をしましょう。
フォルダ構成とフォルダID
前回までのブログを参考にして、「分割済み」フォルダの中に、2つのフォルダを追加しておきます。
- 01_画像化済み
- 02_画像化処理済み元ファイル
このような構成にしておきましょう。
プロパティストアにも、IDを格納しておきます。
const PDF_TO_IMAGE_FOLDER_ID = "あなたのフォルダID"; // 01_画像化済みフォルダ
properties.setProperty("PDF_TO_IMAGE_FOLDER_ID", PDF_TO_IMAGE_FOLDER_ID);
const PROCESSED_IMAGECASH_FOLDER_ID = "あなたのフォルダID"; // 02_画像化処理済み元ファイル
properties.setProperty("PROCESSED_IMAGECASH_FOLDER_ID", PROCESSED_IMAGECASH_FOLDER_ID);
アカウントの準備とAPI Key
PDF.coでアカウントを作成します。(この辺の操作は、割愛します)
ログインすると、ダッシュボードが表示されます。そして、目の前に思いっきりYour API Keyが表示されています。
コピーしてメモしておきましょう。
API Keyもプロパティストアに格納しておきます。
const PDFCO_API_KEY = "あなたのAPI KEY";
properties.setProperty("PDFCO_API_KEY", PDFCO_API_KEY);
メソッドとエンドポイントの確認
PDF.coのPDF to Imageクラスには、画像タイプごとに、4つのメソッドがあります。
- /pdf/convert/to/jpg
- /pdf/convert/to/png
- /pdf/convert/to/webp
- /pdf/convert/to/tiff
今回の目的はOCRなので、pngにします。
エンドポイントは、https://api.pdf.co/v1/pdf/convert/to/png です。
全体的な流れ(要件定義)
前回の、PDFの分割や結合とほぼ同じです。
- 「PDF分割機」の「分割済み」フォルダにPDFファイルがあるか確認する
- あれば、ファイルIDを取得して配列に格納する
- 各PDFファイルをエンドポイントにリクエスト送信する
- 画像化されたファイルをダウンロードする
- for文で回す
- 元のPDFファイルは「処理済み」フォルダに移動する
- 1.を時限トリガーでぶん回す
今回も、要件定義をAIにぶっこみます。
できあがったコード
できあがったコードがこちらです。解説は後にします。とてもシンプルなリクエストになっています。
/**
* 📄PDF画像変換機
* 指定フォルダ内のPDF(1ページに分割済み)を、画像化してフォルダに保存する。
* 元のPDFファイルは処理済みフォルダへ移動する。
*/
function convertPdfToImageByPDFCO() {
//ファルダIDなどを取得しておく
const properties = PropertiesService.getScriptProperties();
const API_TOKEN = properties.getProperty("PDFCO_API_KEY");
const splitFolderId = properties.getProperty("SPLIT_FOLDER_ID");
const pdfToImageFolderId = properties.getProperty("PDF_TO_IMAGE_FOLDER_ID");
const tempFolderId = properties.getProperty("PROCESSED_IMAGECASH_FOLDER_ID"); // 一時処理フォルダ
//API Keyが無かったときのガード節
if (!API_TOKEN) {
console.error("❌ API トークンが取得できませんでした。");
return;
}
//すべてのPDFを取得する
const pdfFolder = DriveApp.getFolderById(splitFolderId);
const pdfFiles = pdfFolder.getFilesByType(MimeType.PDF);
//PDFが無かったときのガード節
if (!pdfFiles.hasNext()) {
console.log("📂 フォルダ内に PDF がありません");
return;
}
//ファイルがなくなるまでwhile分を回す
while (pdfFiles.hasNext()) {
const file = pdfFiles.next();
const pdfBlob = file.getBlob();
const fileName = file.getName();
console.log(`🔄 PDF変換開始: ${fileName}`);
//一時処理フォルダにPDFを保存し、公開リンクを取得
const tempFolder = DriveApp.getFolderById(tempFolderId);
const tempFile = tempFolder.createFile(pdfBlob);
tempFile.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
const pdfFileUrl = tempFile.getDownloadUrl();
//エンドポイントとPayload
const apiUrl = "https://api.pdf.co/v1/pdf/convert/to/png";
const headers = {
"x-api-key": API_TOKEN,
"Content-Type": "application/json"
};
const payload = JSON.stringify({
url: pdfFileUrl,
pages: "0",
outputformat: "png"
});
const options = {
method: "post",
headers: headers,
payload: payload,
muteHttpExceptions: true
};
try {
// API にリクエストを送信
const response = UrlFetchApp.fetch(apiUrl, options);
const jsonResponse = JSON.parse(response.getContentText());
console.log(`📡 API Full Response: ${response.getContentText()}`);
const imageUrl = jsonResponse.urls ? jsonResponse.urls[0] : null;
//変換できなかったときのガード節
if (!imageUrl) {
console.error(`❌ 画像URLを取得できませんでした: ${fileName}`);
continue;
}
console.log(`✅ 変換完了: ${imageUrl}`);
//画像をダウンロードし、Google Drive に保存
const imageResponse = UrlFetchApp.fetch(imageUrl);
const imageBlob = imageResponse.getBlob()
.setName(fileName.substring(0, fileName.lastIndexOf(".")) + ".png");
DriveApp.getFolderById(pdfToImageFolderId).createFile(imageBlob);
console.log(`📂 画像保存完了: ${fileName.substring(0, fileName.lastIndexOf(".")) + ".png"}`);
//処理済みPDFをゴミ箱へ移動
file.setTrashed(true);
console.log(`📂 処理済みフォルダへ移動完了: ${fileName}`);
//待機処理(サーバー負荷軽減)
Utilities.sleep(1000); // 1秒待機
} catch (e) {
console.error(`❌ ${fileName} の処理中にエラー: ${e.toString()}`);
}
}
}
補足解説
PDF.coのエンドポイントへのリクエストには、PDFファイルの公開リンクをPayloadとして持たせる必要があります。ダウンロードリンクではダメです。
なので、公開リンクを取得するために、一時的にGoogle Driveにファイルを作成しています。
また、公開リンクは、誰でも閲覧権限がないといけないので、DriveAppサービスのメンバーを使って、権限を付与しています。
//一時処理フォルダにPDFを保存し、公開リンクを取得
const tempFolder = DriveApp.getFolderById(tempFolderId);
const tempFile = tempFolder.createFile(pdfBlob);
//ファイルに閲覧権限を付与する
tempFile.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
const pdfFileUrl = tempFile.getDownloadUrl();
//中略
const payload = JSON.stringify({
url: pdfFileUrl,
pages: "0",
outputformat: "png"
});
実行してみる
まず、画像化するための元ファイルが、フォルダにあることを確認しましょう。(前回分割したPDFでいいと思います。)
convertPdfToImageByPDFCO()を実行します。手で。
画像化済みフォルダに、pngファイルが作成されていればOKです。
処理済みのファイルも移動して、空っぽになっていればOKです。いかがでしょうか。
まとめ
以上で、「GASでPDFを画像に変換しよう」をお送りしました。
これで、以下の流れが自動化できました。
- PDFをGoogle Driveに保存する
- PDFを単ページに分割する
- PDFを画像に変換する
- 画像をGemini APIで画像認識(OCR)する
実際の業務では、OCRした内容をGeminiで要約するところまで活用しています。ブログを書く時間は~ないかなぁ。。。
いろんな用途に使えそうですね。それではまた。
このシリーズの目次
- [GAS ライブラリ]GASでPDFを分割・結合しよう
- [GAS ライブラリ]GASでPDFを分割・結合しよう その2
- [GAS ライブラリ]GASでPDFを分割・結合しよう その3
- [GAS ライブラリ]GASでPDFを分割・結合しよう その3
- [GAS 外部API]GASでPDFを画像に変換しよう