Translate

2022年8月13日

GASで複数のPDFを結合する

ゾウでもわかる Google Apps Script

Google Apps Script で複数のPDFを結合できないかと探していたら、officeの杜 | PDFを結合するという記事がありました。

officeの杜で共有されているこちらのコード にある mergePdfs() を活用させていただきました。元ネタは、stack overflowにある Merge Multiple PDF's into one PDF です。


結合できるPDFファイル

stack overflowのコメントにPDFのバージョンが1.5以上である場合には大きな修正が必要(結合できない)と記載されていますが、Googleの機能(印刷)やChromeの印刷でPDFにしたPDFはマージ可能でした。

対象 PDFバージョン マージ可否
Googleドキュメント 1.4
Googleプレゼンテーション 1.7
Googleスプレットシート 1.7
Google Chrome 1.4
Office 365 1.7

PDFのバージョンはAdobe Acrobat Readerで確認できます。

下記は、Googleドキュメントの印刷からPDFを保存した場合です。バージョンは1.4となっています。

Googleスプレッドシート、プレゼンテーションは、バージョンが1.7と表示されますが、

前述したように問題なく結合することができます。

Office 365でPDFに変換したファイルは結合できませんでしたが、Googleを中心としたサービスを利用しているのであれば問題なさそうです。


mergePdfs()関数を使う

mergePdfs()関数の説明と引数をみると

mergePdfs(directory, name, pdf1, pdf2, pdf3, ....)
drirectory PDFが格納されているディレクトリID
name 結合したPDFのファイル名
pdf1, ... 結合するPDFファイル

結合するPDFファイルは2つ以上複数指定できますが、引数でファイル名をひとつづつ指定するのは煩わしい。

ということで、フォルダーにあるPDFをリスト(配列に格納)して引数として渡せるように変更してみました。

またPDFファイルの結合順番はファイル名で昇順にソートした順番にします。ファイル名の先頭に数字を入れておけば番号順で結合されます。

例えば、ファイル名を

0.test.pdf
1.test.pdf
2.test.pdf

としておけば、0.test、1.test、2.testの順番で結合されます。


mergePDFs()関数の修正

先ずは、office杜のこちらのリンクからmergePdfs()のプログラムを取得してください。

修正箇所は3箇所です。

12行目

関数の引数をファイル名から、配列を1つ渡すように変更します。

修正前:
function mergePdfs(directory, name, pdf1, pdf2, opt_pdf3) {
修正後:
function mergePdfs(directory, name, pdfList {
21行目

関数の引数ループで取得しているところを、引数に格納されているファイル数分(配列の長さ分)で処理するように変更します。

修正前:
for (var argumentIndex = 2; argumentIndex < arguments.length; argumentIndex++) {
修正後:
for (var i=0; i<pdfList.length; i++) {
23行目

マージするPDFのファイルサイズ取得を引数の配列からに変更します。

修正前:
var bytes = arguments[argumentIndex].getBlob().getBytes()
修正後:
var bytes = pdfList[i].getBlob().getBytes()

また、475行目移行はPDFを分割する関数のコードになっていますので削除してしまっても問題ありません。


mergePDFs()関数を呼び出す

次は、修正したmergePDFs()関数を呼び出すプログラムになります。指定したフォルダIDにあるPDFファイルを配列(pdfList)に格納して、ファイル名順にソートしてから mergePDFsに引き渡します。

<フォルダID>にはGoogleドライブのフォルダIDを、<ファイル名>には結合して作成するファイル名にしてください。

フォルダIDは、フォルダを開いたときのURL、https://drive.google.com/drive/folders/XXXXX XXXX部分になります。

function merge(srcFolderId, fileName){
  var srcFolderId = '<フォルダID>' //フォルダ ID
  var fileName = '<ファイル名>' //結合後のファイル名

  //フォルダ内のファイルを取得
  var srcFolder = DriveApp.getFolderById(srcFolderId)
  var srcFiles = srcFolder.getFiles()

  //PDFファイルだけを配列に格納
  var pdfList = []
  while(srcFiles.hasNext()) {
    var srcFile = srcFiles.next()
    if (srcFile.getMimeType()==='application/pdf') {
      pdfList.push(srcFile);
    }
  }
  pdfList.sort()  //照準でソート
  mergePdfs(srcFolder, fileName, pdfList)
}

13行目のgetMimeType()でPDFファイルだけを対象にしています。また17行目のsort()で配列に格納した各ファイルをファイル名でソートしています。


スプレッドシートのUI機能で汎用的にする

もう少し汎用的に使えるように、スプレッドシートのUI機能を使って、フォルダIDと結合して作成するファイル名をUI上で入力するようにします。

またスプレッドシートのメニューからGASの実行を可能にします。

スプレッドシートの機能を使うので、Googleスプレッドシートの拡張機能から Apps Script を作成する必要があります。


スプレッドシートにメニューに追加する

スプレッドシートが開かれたときにメニューを追加します。こんな感じです。

「mergePDFs」 > 「PDFを結合」 で diaLog関数を実行します。

function onOpen(){ 
  SpreadsheetApp
    .getActiveSpreadsheet()
    .addMenu('mergePDFs', [
      {name: 'PDFを結合', functionName: 'diaLog'}
    ])
}

ダイアログ

詳細の説明は省きますが、「フォルダーID」と「ファイル名」の入力の後に確認画面を出してが簡単なチェックと確認を出力して merge()関数にフォルダIDとファイル名を引き渡します。

ダイアログでフォルダーIDを指定する

またPDFの結合でエラーとなった場合にもエラー出力をするようにしています。

ダイアログでエラーメッセージを出力

下記がサンプルコードとなります。

function diaLog() {
/********************
PDFがあるフォルダを指定 
********************/
  var srcFolderId = Browser.inputBox("結合したいPDFがあるフォルダを指定してください(FolderID)",Browser.Buttons.OK_CANCEL)
  //キャンセルが押下されたら終了
  if (srcFolderId == 'cancel')  {
    return
  }
  try {
    var srcFolder = DriveApp.getFolderById(srcFolderId)
  }
  catch(e) {
    Browser.msgBox('ERROR', srcFolderId + ' is not available', Browser.Buttons.OK)
    return
  }
/********************
ファイル名の指定 
********************/
  var fileName = Browser.inputBox("結合後のファイル名を指定してください(FolderID)",Browser.Buttons.OK_CANCEL)
  //キャンセルが押下されたら終了
  if (fileName == 'cancel')  {
    return
  } else if (fileName == '') {
    Browser.msgBox('ERROR', 'File name is not defined', Browser.Buttons.OK)
    return
  }
/********************
開始の確認 
********************/
  var srcFolderName = srcFolder.getName()

  var result = Browser.msgBox('Confirm', '「' + srcFolderName + '」にあるすべてのPDFを\\n「' + fileName + '」として結合します', Browser.Buttons.OK_CANCEL)
  if (result == 'cancel')  {
    return
  }

  try {
    merge(srcFolderId, fileName)  //PDF格納フォルダ,ファイル名
  }
  catch(e) {
    Browser.msgBox('ERROR', 'Error Occurred during merging', Browser.Buttons.OK)
    return
  }
  
  Browser.msgBox('Complete', 'PDFの結合が完了しました', Browser.Buttons.OK)
}

これで完成です。

スプレッドシートのメニューから dialog()関数を呼び出し、merge()関数でフォルダ内のPDFをリスト化して、mergePDFs()関数でPDFを結合します。

先人の知恵と努力に感謝です。

2022年8月11日

アップライト・ベースの練習にチューナーは必須アイテム

Ibanez UB804 with KORG AW-LT100B(表面)

エレクトリック・アップライト・ベース(Electric Upright Bass)の練習では、練習前のチューニングだけではなく、練習中にもチューナーを使ってピッチを確認しましょう。


チューナーの仕組み

チューナーの仕組みには下記の3種類の方式があります。

名称 方式
ピエゾ(Piezo) 楽器の振動でチューニング(本体に取り付ける必要あり)
マイク(Mic) マイクで拾った音(振動)でチューニング
ライン入力 電気信号でチューニング(シールドで接続が必要)

ヘッドにクリップで挟むクリップチューナー(ピエゾ式)は、EUBの練習に使える手軽なチューナーです。


ベース専用チューナーを使おう

低音を瞬時に拾ってくれるベース専用チューナーがオススメです。

の2つで悩みました。

両方ともクリップ式の低音楽器専用のチューナーで、可動範囲が広いのでディスプレイを自分の見やすい位置に設定できます。

下記は、KORG AW-LT100Bの可動域です。

KORG AW-LT100Bの可動域

ダダリオのチューナーは駆動時間が分かりませんでしたが、単4電池1本で100時間駆動するKORG AW-LT100B を購入。これなら、練習中にずっと点けっ放しにできます。

KORG AW-LT100B
  • 低音の振動もしっかり拾えるベース専門チューナー
  • 可動範囲が広い(見やすい位置に設置できる)
  • 単4電池1本で100時間駆動

測定範囲は、E0(20.60Hz)〜 C5(523.3Hz)です。A=440Hz でチューニングするとベースの各開放弦の周波数は下記の通りです。

開放弦 音(周波数)
1弦(G弦) G1(97.999Hz)
2弦(D弦) D1(73.416Hz)
3弦(A弦) A1(55.0Hz)
4弦(E弦) E1(41.203Hz)

KORG AW-LT100B の使用感

コントラバスであればクリップをブリッジ(駒)に挟むのでしょうが、Ibanez UB-804 の場合は、ヘッドに取り付けます。

Ibanez UB804 with KORG AW-LT100B(裏面)

チューニング時の注意点は、3弦開放弦のA1の周波数は55Hzですが、ディスプレイ上にはAとしか表示されません。1オクターブ上のA2(110Hz)でも表示上はAなので、ベース初心者だと最初は戸惑うかもしれませんが、慣れの問題です。

基準音のAは、436〜445Hzで1Hz単位で変更でき、電源をOFFにしても記憶されています。ディスプレイの左上に常に表示されているので基準音が何になっているかを確認しましょう。

KORG AW-LT100Bは、ベース専用のチューナーということもあり、低音への反応はとてもよいです。そうは言っても早いテンポでは音が拾えないので、メトロノームを♩= 60-66 にして、やや遅いテンポでのスケール練習で一音一音を確認しましょう。

また、ピエゾ式のチューナーは、振動で音程を判定しているので、ハイポジションで弾く高音では、弦の振動幅が少なくなりチューナーが音を拾いづらくなります。

機能的には、C5(523.3Hz)まで測定可能とありますが、UB804では、1弦21フレット(E4 329.628Hz)ぐらいまでが、チューナーで音程を拾える範囲です。

しかし、E4まで測定できるということは、ハイポジションでのCからEまでのスケール練習でチューナーを使った練習ができるということです。この範囲でチューナー使えれば十分です。


EUB初心者は練習でチューナーは必須

EUB(Electric Upright Bass)にはフレットがないので、自分が出している音程を確認するためにチューナーは必須アイテムです。「象と散歩: アップライト・ベースの運指トレーニング」で紹介しているスケール練習で、1音1音、音程を確認しながら練習しましょう。

特に、ハイポジション(サムポジション)では、半音ごとの間隔が狭くなるのと、親指を使った弾き方になるので、音程がブレブレになります。しっかりと一音づつチューナーで音程を確認しながら練習しましょう。


練習でチューナーを使う理由

チューナーを見ながら練習するのは、A=440Hzのピッチで演奏しなければならないということではありません。テンポやピッチがジャストでないことが、グルーヴ感や味となります。

しかし、独りよがりの気持ち良さではなく、他の楽器と合わせたときのアンサンブルを大切にしなければ音楽は成立しません。

そういう意味で、無骨なまでに融通の効かないチューナーを使って音程を確認することが、他の楽器とのアンサンブルでのピッチの合わせ方につながります。

練習中にメトロノームを使う理由も同じです。メトロノームを使った練習については、「象と散歩: 最高に可愛い機械式メトロノームでリズム感を鍛える」を参考にしてください。

チューナーとメトロノームを使ってスケール練習することが、ウォーキング・ベース上達への道のりと信じて練習しましょう(自分への戒め)。

アクセス上位(過去7日間)