PR

Node.jsを使って複数のwebp形式の画像を一つのPDFファイルに変換する

未分類
記事内に広告が含まれています。
スポンサーリンク

テストで撮り溜めたスクリーンショットを簡単にPDFファイルにできないかなと思い、ツールを作ってみました。

スポンサーリンク

前提

  • node.js
  • fs
  • pdfkit
  • sharp

webp形式からjpeg形式への変換にsharpを使っています。また、jpeg画像をPDF化するのにpdfkitを使っています。

やりたいこと

inフォルダに入れた画像を集約して、output.pdfとしてoutフォルダに出力します。
画像がwebpの場合を考慮して、一度jpegに変換した画像をPDFにしています。

コード

私の力不足で、どうしても同期処理がうまく書けず、3部構成で実装しました。
index.js、index2.js、index3.jsの順にnodeで実行する形です。

  • index.js : inフォルダのwebp形式の画像をjpegに変換してout/jpegに保存する
  • index2.js : out/jpegの画像をPDFファイルout/output.pdfに出力する
  • index3.js : in並びにout/jpegフォルダを空にする

index.js

inフォルダにある画像データを、jpeg形式に変換してout/jpegフォルダに保管します。

const sharp = require("sharp");
const fs = require("fs")
const files = fs.readdirSync('./in')

function webpToJpeg () {
  return new Promise((resolve, reject) => {
    for (let index = 0; index < files.length; index++) {
      const file = files[index];
      if (file.indexOf('webp') != -1) {
        const inpath = `./in/${file}`
        const outpath = `./out/jpeg/${file.split('.')[0]}.jpeg`
        sharp(inpath).toFile(outpath)
      }
    }
    resolve()
  })
}

webpToJpeg()

index2.js

out/jpegフォルダにあるjpeg形式の画像を、1つのPDFファイルに変換して、outフォルダにoutput.pdfとして出力します。

const fs = require("fs")
const PDFDocument = require('pdfkit');
const doc = new PDFDocument({ size: 'A4'})

function jpegToPDF () {
  const outjpg = fs.readdirSync('./out/jpeg/')
  for (let index = 0; index < outjpg.length; index++) {
    const element = outjpg[index];
    if (element.indexOf('jpeg') != -1) {
      const elempath = `./out/jpeg/${element}`
      doc.image(elempath, 0, 0, { fit: [600, 850], align: 'center', valign: 'center' })
      doc.addPage();
      doc.save()
    }
  }
}
jpegToPDF()

doc.pipe(fs.createWriteStream('./out/output.pdf'))
doc.end()

index3.js

inフォルダ、out/jpegフォルダにある画像データをすべて削除します。

const fs = require('fs')
const path = require('path')

const dirIn = './in'
const dirOutJpeg = './out/jpeg' 

fs.readdir(dirIn, (err, files) => {
  if (err) throw err

  for (const file of files) {
    fs.unlink(path.join(dirIn, file), err => {
      if (err) throw err
    })
  }
})

fs.readdir(dirOutJpeg, (err, files) => {
  if (err) throw err

  for (const file of files) {
    fs.unlink(path.join(dirOutJpeg, file), err => {
      if (err) throw err
    })
  }
})

悩んだところ

反復処理をforEachでやろうとしたら、反復処理が完了する前にPDF出力してしまい、同期処理って難しいな、なんでだろうとハマり、結局諦めて、人間系で処理が完了したのを確認して、次の処理を実行することにしました。

終わりに

とりあえず動くところまでやりました。このツールでの処理時間は想像以上に早く、以前に紹介した「PDFで印刷」する方法の5倍くらいは早いです。とはいえ、もっとスマートなやり方があると思うので、githubで公開して、知見をいただきたいと思います。

コメント