3
3

More than 3 years have passed since last update.

ランダムなページサイズのPDFファイルを作成する

Posted at

仕事で各ページのサイズが異なる(1ページ目がA4で、2ページ目がB5 など)PDFファイルが必要になりました。

各ページごとにサイズが異なるPDFファイルを作成できるようなツールはパッと見た感じ手近にはなかったので、Pythonで自作しました。

準備するもの

なお、使用した環境はWindows 10 Home 1903Verです。

  • Python(3.7.5)
  • PyPDF2
  • ReportLab
  • tqdm(使わなくてもいいです)

PyPDF2は空のPDFページを作ることができます。ただし、作成したページに文章や画像など任意のオブジェクトを書き込むことはできません

ReportLabは直接プログラムコードから文字や図形などを書き込んだPDFを作ることができます。ただし、(自分の見た限り)各ページのページサイズが異なるPDFを作ることはできません

なので、大まかには

  1. ReportLabで1ページだけのPDFファイルを作成する
  2. PyPDF2.PdfFileReaderで1を読み込む
  3. PyPDF2.PdfFileWriterで作成したPDFに、2を貼り付ける

という手順をとります。ちょっと面倒くさいですね。

コード

今回A0~C10(Cなんてあるんですね、はじめて知りましたが)まですべてのサイズのページをまんべんなく突っ込みたいので、reportlab.lib.pagesizesの中で定義されているページサイズをすべて取得してみました。

testpdf_creator.py
import random
import os
from pathlib import Path

from tqdm import tqdm
from PyPDF2 import PdfFileReader, PdfFileWriter
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import reportlab.lib.pagesizes

YU_GOTHIC = Path(filter(lambda n: (Path(n) / "Fonts" / "YuGothB.ttc").exists(), os.environ.get("PATH").split(";")).__next__()) / "Fonts" / "YuGothB.ttc"

def makepage(writer, size, name):
  A4 = reportlab.lib.pagesizes.A4
  # テキストの書かれたPDFを作る
  c = canvas.Canvas("base.pdf", pagesize=A4)
  pdfmetrics.registerFont(TTFont("YU", YU_GOTHIC))
  c.setFont('YU', 20)
  c.drawString(12, 12, f"This is {name} page.")
  c.drawString(12, A4[1] - 50, f"This is {name} page.")
  c.rect(0, 0, A4[0], A4[1])
  c.showPage()
  c.save()

  # Create Page
  with open("base.pdf", mode="rb") as f:
    r = PdfFileReader(f)
    p = writer.addBlankPage(size[0], size[1])
    scale = [a / b for a, b in zip(size, A4)]
    p.mergeTransformedPage(r.getPage(0), [scale[0], 0, 0 , scale[1], 0, 0] , True)

if __name__ == "__main__":
  writer = PdfFileWriter()
  sizes = list(filter(lambda n: type(eval(f'reportlab.lib.pagesizes.{n}')) == tuple, dir(reportlab.lib.pagesizes)))
  s = random.sample(sizes, 10)
  for n in tqdm(s):
    makepage(writer, eval(f"reportlab.lib.pagesizes.{n}"), n)

  with open("multisize.pdf", mode="wb") as f:
    writer.write(f)

気をつけたところ

ReportLabのCanvasについて

ReportLabでは、Canvasクラスを使うことでPDFファイルを作成することができます。
ただし、ここで取得できるキャンバスでは左下が原点(X:0 Y:0)になっていますので、図形を描画する際に注意が必要です。

それが嫌な場合は、Canvasクラスのコンストラクタにbottomup=Falseを追加します。

Canvasオブジェクトのメソッドについて

Canvasオブジェクトのメソッドrect()では、次のようにX, Y座標、縦横幅のあとにいくつか引数をとります。

reportlab/pdfgen/canvas.py
    def rect(self, x, y, width, height, stroke=1, fill=0):
        "draws a rectangle with lower left corner at (x,y) and width and height as given."

ただ、この引数のstorke, fillなどという値は、線の幅や色などの値を示しているわけではなく、色を塗るかどうかのBoolean型です

これについてはreportlab/pdfgen/canvas.pyの44行目に以下の記述があります。

reportlab/pdfgen/canvas.py
PATH_OPS = {(0, 0, FILL_EVEN_ODD) : 'n',  #no op
            (0, 0, FILL_NON_ZERO) : 'n',  #no op
            (1, 0, FILL_EVEN_ODD) : 'S',  #stroke only
            (1, 0, FILL_NON_ZERO) : 'S',  #stroke only
            (0, 1, FILL_EVEN_ODD) : 'f*',  #Fill only
            (0, 1, FILL_NON_ZERO) : 'f',  #Fill only
            (1, 1, FILL_EVEN_ODD) : 'B*',  #Stroke and Fill
            (1, 1, FILL_NON_ZERO) : 'B',  #Stroke and Fill

rectメソッドはこのPATH_OPSという変数を参照しており、strokefillという二つの引数はこの配列にある値二つに対応しているようです。

PyPDF2.PageObjectmergeScalePageについて

PyPDF2.PdfFileWriterでPDFに新しいページを作成するには、PdfFileWriter#addBlankPage()メソッドを使います。

ここに別のページオブジェクトを貼る場合はPageObject#merge***Page()を使います。

縮尺を変えるだけだと一見mergeScaledPage()あたりでいけそうですが、このメソッドのscaleは一つの値しか受け付けないため、縦と横の縮尺が違う場合には利用できません。

縦と横の縮尺が異なる場合は、mergeTransformedPage()を使えば良いです。というのも、mergeScaledPage()は、縦横の倍率を同一値にしたmergeTransformedPage()を呼んでいるだけなので。

もし縦を1.5倍、横を2倍の倍率に設定したい場合は、次のようにすれば良いです。

page.mergeTransformedPage('追加したいPageObject', [2, 0, 0 , 1.5, 0, 0] , True)

現存するすべてのページサイズの値を取得するには

reportlab/lib/pagesizes.pyには、すべてのページサイズの値を定義したtupleが記述されていますので、これをdir関数で取得しています。

ただし、このファイルにはページの縦横を逆転させるportlaitlandscapeなどの関数も定義されているため、filter関数でそれらを取り除きます

list(filter(lambda n: type(eval(f'reportlab.lib.pagesizes.{n}')) == tuple, dir(reportlab.lib.pagesizes)))

参考文献

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3