[GAS ライブラリ]GASでPDFを分割・結合しよう

GASでPDFの分割と結合GAS

どうも。Kenny(tsujikenzo)です。このシリーズでは、 「GASでPDFを分割・結合しよう」 を全3回でお送りします。2025年も、絶好調です。

はじめに

みなさん、日々GASで業務効率化に励んでいると思います。

そんな、GASによる業務効率化で鬼門なのが、 「PDFの分割と結合」 です。

Pythonには便利なライブラリがありますが、GASだと公式のサービスだけでは物足りません(2025年2月現在)。

今回は、「GASでPDFを分割・結合しよう」をお届けします。

ちなみに、BaseサービスのBlobインターフェイス(スプレッドシートやドキュメントなど、GWSで提供されているサービスのオブジェクトをBlobオブジェクトとして操作する)については、こちらの記事を参考にしてください。

GASのUtilities.newBlob()メソッドは、PDFに対応していません。なので、以下のいずれかの方法が必要です。という話です。

  1. pdf-lib.jsライブラリを使う ⇒ 今回の目的
  2. GoogleドキュメントをPDFに変換する
  3. GoogleスプレッドシートなどのURLをfetchする
  4. HTMLをPDFに変換する

ライブラリ「PDF-LIB」

PDF-LIBは、JavaScriptでPDFを生成したり処理できる、オープンソースライブラリです。

軽量で、誰でも使えるため、サーバーやWEBアプリなどさまざまな場面で使われています。

2020年に、GASがV8に対応したため、GASでもPDF-LIBが使えるようになりました。

公式リファレンスはこちらです。PDFへの画像の挿入やフォーム処理など、いろんなことができます。

301 Moved Permanently

今回は、「分割」と「結合」に特化してお送りします。

2種類のPDF-LIB

pdf-libは、2種類のソースコードを公開しています。

実装されているメソッドなどを確認するばあいは、非圧縮版を参照しましょう。

ただし、オリジナルのライブラリにはsetTime()メソッドが使われており、これがGASで使えないため、正常に動作しません(エラーは出ないのですがただしく処理されません。)

GAS用に書きかえたものを、わたしのGitHubに公開しましたので、こちらを参照ください。

下準備

GASでpdf-libを使うばあいは、上記ソースコード全文をコピーして、スクリプトファイルに貼り付けるだけです。 

これで、必要な処理を呼び出すだけで、PDFを処理できます。

処理を行うフォルダIDなどは、事前にプロパティストアに格納しておきましょう。

const FOLDER_ID = "あなたのフォルダID";
PropertiesService.getScriptProperties().setProperty("FOLDER_ID",FOLDER_ID);

それと、テキストのみの単ページのPDFを、2枚準備しておきましょう。こちらもそれぞれのファイルIDをプロパティストアに格納しておきます。

ちょっと長いですが、以下をコピペして実行していただければ。

//単ページのドキュメントを作成する
 let doc = DocumentApp.create('サンプルGoogleドキュメント1');
 let body = doc.getBody();
 body.appendParagraph("このPDFはGoogleドキュメントから作られており、このページは1ページ目です");

//変更を保存
 doc.saveAndClose();

 let docId = doc.getId();
 let docFile = DriveApp.getFileById(docId);

//GoogleドキュメントをPDFに変換
 let pdfBlob = docFile.getAs(MimeType.PDF);
 pdfBlob.setName('サンプルPDF1');

//Google Driveに保存
 let pdf = DriveApp.getFolderById(folderId).createFile(pdfBlob);

//ファイルIDをプロパティストアに格納する
 const PAGEPDF_ID = pdf.getId()
 PropertiesService.getScriptProperties().setProperty("PAGEPDF_ID", PAGEPDF_ID);
 console.log(PropertiesService.getScriptProperties().getProperty("PAGEPDF_ID"));


//単ページのドキュメントを作成する2
 doc = DocumentApp.create('サンプルGoogleドキュメント2');
 body = doc.getBody();
 body.appendParagraph("このPDFはGoogleドキュメントから作られており、このページは2ページ目になる予定です");

//変更を保存
 doc.saveAndClose();

 docId = doc.getId();
 docFile = DriveApp.getFileById(docId);

//GoogleドキュメントをPDFに変換
 pdfBlob = docFile.getAs(MimeType.PDF);
 pdfBlob.setName('サンプルPDF2');

//Google Driveに保存
 pdf = DriveApp.getFolderById(folderId).createFile(pdfBlob);

//ファイルIDをプロパティストアに格納する
 const PAGEPDF_ID2 = pdf.getId()
 PropertiesService.getScriptProperties().setProperty("PAGEPDF_ID2", PAGEPDF_ID2);
 console.log(PropertiesService.getScriptProperties().getProperty("PAGEPDF_ID2"));

フォルダにPDFが2枚作られていたらOKです。もちろん、手動でPDFを準備していただいても構いません。 

プロパティストアの運用がめんどくさい人は、ソースコードに直書きでも構いませんからね。

非同期処理

あまり使用する機会がありませんが、GASは、JavaScript同様に非同期処理が使えます。

非同期処理とは、重要な処理は待つが、プログラム全体は止まらないという仕組みです。

PDF-LIBのばあい、PDFの読み込みや書き込みなどが終わるまで次の処理を行わない、という約束(promise)が大切です。

ライブラリのソースコードには、数多くのPromiseが実装されています。awaitの指示を付けると、Promiseの完了を待ってから次の処理を実行できます。

非同期処理の使い方

awaitを使えるようにするために、functionの前にasync(非同期ですよ)という指示が必要です。

メソッドにPromise処理がされているかどうかは、呼び出し側では分かりません。リファレンスなどを確認する必要があります。

async function myAsyncFunction() { //✅関数を`async`にすることで `await`を使えるようにする

// 非同期処理を行う
 const pdf = await PDFLib.PDFDocument.load(bytes); //✅ `await` でPDFのロードを待つ
//load()メソッドの中身にはPromiseを返す処理が書かれている
}

Uint8Array

Uint8Arrayとは、バイナリデータ(PDFなど)を扱うための特別な配列です。

PDFは0と1の組み合わせでできており、そのままではGAS(JavaScript)で直接扱うのが難しいため、一度Uint8Arrayに変換してから処理します。

PDF-LIBの.load()メソッド(PDFを読み込むためのメソッド)も、Uint8Arrayを受け取ることで、正しくファイルを読み込めるようになっています。

//PDFファイルのブロブを取得
 const blob = DriveApp.getFileById(fileId).getBlob();

//ブロブ(バイナリデータ)をバイト列(Uint8Array)に変換
 const bytes = new Uint8Array(blob.getBytes()); 

// PDFを読み込む
 const pdf = await PDFLib.PDFDocument.load(bytes);

後ほど紹介しますが、.save()メソッドもUint8Arrayを返しますので、直接ブロブを作成し、Google Driveへ保存可能です。

//Google Driveにファイルを生成する
 const pdfBytes = await mergedPdf.save();
 const finalBlob = Utilities.newBlob(pdfBytes).setContentType("application/pdf");
 DriveApp.getFolderById(folderId).createFile(finalBlob);

ファイルタイトルを取得してみよう

それでは、最後にPDF-LIBを使って、ファイルタイトルを取得してみましょう。

ファイルを操作するのは、PDFDocumentクラスです。

PDFDocumentoクラスの主なメンバーはこちらです。 

ファイルタイトル取得

ファイルのタイトルを取得するのは、.getTitle()メソッドです。

以下のような処理で、タイトルを取得できます。ついでに、PDFファイルの著者名も取得してみましょう。

async function getFileInfomation() {

//プロパティストアからPDFのファイルIDを取得する
 const fileId = PropertiesService.getScriptProperties().getProperty('PAGEPDF_ID');

//PDFファイルのブロブを取得
 const blob = DriveApp.getFileById(fileId).getBlob();

//ブロブ(バイナリデータ)をバイト列(Uint8Array)に変換
 const bytes = new Uint8Array(blob.getBytes()); 

// PDFを読み込む
 const pdf = await PDFLib.PDFDocument.load(bytes);// ✅ `await` をつけることでPDFのロードを待つ

//PDFファイルの著者を取得
 const author = pdf.getAuthor();
 console.log(author);

//PDFファイルのタイトルを取得
 const title = pdf.getTitle();
 console.log(title);
}

PDFのファイル名が取得できていればOKです。※著者名は設定されていませんでした。(;^_^A 

まとめ

以上で、「GASでPDFを分割・結合しよう」の1回目をお送りしました。

ライブラリって便利ですよね。オープンソースコードの開発をしている(ほぼ)ボランティアの方々はすごいと思います。

次回は、「PDFを結合しよう」をお届けします。お楽しみに。

参考資料

このシリーズの目次

タイトルとURLをコピーしました