【GAS】行のデータを縦に並べてPDF化する

GAS pdf

行ごとにデータが自動的に溜まっていくスプレッドシートで、1行のデータをプリントアウトして使いたい時のGoogle Apps Scriptを書きました。

Google Formsで溜まっていくデータをデータごとにプリントアウト用に出力したい時や、応用効かせて書類のフォーマットに当てはめて請求書や納品書としてPDF出力させたいときなんかに使えると思います。

こんな感じのデータから

下記のようなレイアウトにデータ出力してPDF化するGASになります。

GASの作成

メニューに追加用のメニュー.gsとPDF作成用のPDF作成.gsを準備。

GAS pdf

基本のコード内容は下記のページを参考にしました。コードの詳細や設定方法はこちらもみてみて下さい。

【コピペでOK!】GASで現在のシートをPDF化する方法 – Yuki’s bnb blog

データをまとめてるdata,PDFのテンプレートとなるPDFシートを追加して使います。

PDF作成.gs


function confirmSavePdf(){
  //確認メッセージボックスを表示する
  //「キャンセル」をクリックすると保存せずにGASを終了する
  let confirmation = Browser.msgBox("本当にPDFを作成しますか?", Browser.Buttons.OK_CANCEL);
 
  if(confirmation == "cancel") {
    Browser.msgBox("操作をキャンセルしました");
    return false;
  }
  return true;
}


function savePdf(){
   //確認メッセージボックスを表示する
  //「キャンセル」をクリックすると保存せずにGASを終了する
  if(!confirmSavePdf()) return;


  //PDFの保存先
  //★★★フォルダーIDを入力してください★★★
  let folderId = "1wjvV.....cKk9MAJy";
  //https://drive.google.com/drive/folders/1wjvV.....cKk9MAJy

  //アクティブなスプレッドシートを取得する
  let ss = SpreadsheetApp.getActiveSpreadsheet();

  //スプレッドシートIDを取得する
  let ssId = ss.getId();

  //シートIDを取得する
  let shId = ss.getSheetId()||'1cy57lxJh......9bOuW-4';
  
  // データシート
  const datash = ss.getSheetByName('data');

  // テンプレートシート
  const pdfsh = ss.getSheetByName('PDF');
  const pdfshId = pdfsh.getSheetId();

  pdfsh.clearContents();//クリア

  // 見出し行
  const range = datash.getRange('A1:AP1');
  const headers = range.getValues()[0];
  // Logger.log(headers);

  
  //行列データ変換
  // https://note.com/kohaku935/n/n3cde2f8f23d8
  const transpose = array => array[0].map((col, i) => array.map(row => row[i]));

  const lastRow = datash.getLastRow();
  const data = datash.getRange('A2:AP'+lastRow).getValues();
  let row = 1;
  let lineCount = 2;
  const dataLength = headers.length;
  
  headers[headers.length]='';//スペース用

  data.forEach((line)=>{

    //プリントアウトチェックが入ってない時
    if(line[0]===false||line[0]==='') {
      line[line.length]="";//改ページのための空白調整
      let printData = [headers,line];
      pdfsh.getRange(row,1,line.length,2).setValues(transpose(printData));
      //プリントアウトしたものとしてチェック
      datash.getRange(lineCount,1,1,1).setValue(true);
      //console.log(line);
      row+=dataLength+1;
    }


    lineCount++;
  })
  
  // const staySec = 3;
  // Utilities.sleep(staySec * 1000);

  //★★★PDFのファイル名を入力してください★★★
  //※ポイント: ファイル名が重複しないようにしましょう
  let fileName = "印刷用"+"_"+ Utilities.formatDate(new Date(),"JST","yyyyMMdd_hhmmss");
  
  //関数createPdfを実行し、PDFを作成して保存する
  createPdf(folderId, ssId, pdfshId, fileName);
}

//PDFを作成し指定したフォルダーに保存する関数
//以下4つの引数を指定する必要がある
//1: フォルダーID (folderId)
//2: スプレッドシートID (ssId)
//3: シートID (shId)
//4: ファイル名 (fileName)
function createPdf(folderId, ssId, shId, fileName){
  SpreadsheetApp.flush();//PDFデータが真っ白になるのを防止する

  //PDFを作成するためのベースとなるURL
  let baseUrl = "https://docs.google.com/spreadsheets/d/"
          +  ssId
          + "/export?gid="
          + shId;
 
  //★★★自由にカスタマイズしてください★★★
  //PDFのオプションを指定
  let pdfOptions = "&exportFormat=pdf&format=pdf"
              + "&size=A4" //用紙サイズ (A4)
              + "&portrait=true"  //用紙の向き true: 縦向き / false: 横向き
              + "&fitw=true"  //ページ幅を用紙にフィットさせるか true: フィットさせる / false: 原寸大
              + "&top_margin=0.50" //上の余白
              + "&right_margin=0.50" //右の余白
              + "&bottom_margin=1" //下の余白
              + "&left_margin=0.50" //左の余白
              + "&horizontal_alignment=CENTER" //水平方向の位置
              + "&vertical_alignment=TOP" //垂直方向の位置
              + "&printtitle=false" //スプレッドシート名の表示有無
              + "&sheetnames=false" //シート名の表示有無
              + "&gridlines=false" //グリッドラインの表示有無
              + "&fzr=false" //固定行の表示有無
              + "&fzc=false" //固定列の表示有無;

  //PDFを作成するためのURL
  let url = baseUrl + pdfOptions;

  //アクセストークンを取得する
  let token = ScriptApp.getOAuthToken();

  // console.log('url:'+url);
  // console.log('token:'+token);

  //headersにアクセストークンを格納する
  let options = {
    headers: {
        'Authorization': 'Bearer ' +  token
    }
  };
 
  //PDFを作成する
  let blob = UrlFetchApp.fetch(url, options).getBlob().setName(fileName + '.pdf');
 

  //PDFの保存先フォルダー
  //フォルダーIDは引数のfolderIdを使用します
  let folder = DriveApp.getFolderById(folderId);

  //PDFを指定したフォルダに保存する
  folder.createFile(blob);

  console.log('created:' + fileName+'.pdf')
}

メニュー.gs (スプレッドシートにメニュー追加)

function onOpen() {
  let ui = SpreadsheetApp.getUi()
  
  //メニュー名を決定
  //★★★メニュー名は好きなものに置き換えてください★★★
  let menu = ui.createMenu("GAS実行");
  
  //メニューに実行ボタン名と関数を割り当て
  //★★★実行ボタン名は好きなものに置き換えてください★★★
  menu.addItem("PDF作成","savePdf");
  
  //スプレッドシートに反映
  menu.addToUi();
}

PDF出力のGASの修正ポイント

既に出力済みか判別する

新規データのみPDF出力させたかったので、一列目はチェックボックスを入れておいて、チェックを入れてないものを出力するようにしてます。

//プリントアウトチェックが入ってない時     
if(line[0]===false||line[0]===''){

出力したPDFのファイル名が重複しないように

日付と時間をファイル名に加えるようにしました。


  let fileName = "印刷用"+"_"+ Utilities.formatDate(new Date(),"JST","yyyyMMdd_hhmmss");

改ページの調整

下記コードの下の余白で改ページを調整しました。それでもデータ内容によっては改行位置がずれるので、出力したPDFの印刷時に改行確認、微調整をお勧めします。

+ "&bottom_margin=1" //下の余白

行列の変換

[GAS] Googleスプレッドシートの表の行列を反転させる|こはた|note

上記ページに書かれている行列の変換の下記コードの関数を利用して、1行目の見出し行とデータを1つの配列にして、縦長の表示にしてます。

  const transpose = array => array[0].map((col, i) => array.map(row => row[i]));

PDF出力した時に真っ白になるのを修正するGAS

SpreadsheetApp.flush();を追記して修正できました。

今後、請求書、納品書、納品伝票などに使えそう

レイアウトはPDFシートをテンプレートとして、下記のデータを挿入してるところをセル指定で入力するように修正したらいけると思います。

      pdfsh.getRange(row,1,line.length,2).setValues(transpose(printData)); 

データを印刷用に出力するのは紙を使うことを想定してるので、SDGsな時代に反していて心苦しいのですが、そういうニーズもまだあるかと思うので、使えそうならぜひ使ってみて下さい。

GASの詳細はこの本をみてみて下さい。