2022/12/16

【Python】画像のみの固定レイアウトのePubをPDFに変換する

だいぶ前から漫画などの固定レイアウトEpubを目次付きPDFに変換するプログラムをPythonで作りたい、ということで色々と検討してきました。 前回前々回の記事で紹介した内容になりますが、PythonのライブラリのEbookLibimg2pdfが使い勝手が良さそうだということがわかりました。

そこで本記事では、流行りの「ChatGPT」に、プログラムを作成してもらいました。
「画像のみの固定レイアウトepubを、PDFに変換するプログラムをPythonのebooklibとimg2pdfを使って作成してください」とチャットに送信した結果、以下の動画のようになりました。実際には、複数回実行して、ある程度欲しい結果になるまで繰り返し実行しています。



ChatGPTに何回か出力させたプログラムを組み合わせて、さらに足りてない部分などを加えて、修正したものが以下になります。

import img2pdf
import ebooklib
from ebooklib import epub
# 1. EPUBを読み込む
book = epub.read_epub('input.epub')
# 2. EPUBの各ページを画像として取得する
items = []
for item in book.get_items():
if item.get_type() == ebooklib.ITEM_COVER:
items.append(item.get_content())
elif item.get_type() == ebooklib.ITEM_IMAGE:
items.append(item.get_content())
# 3. 画像をPDFに変換する
with open('output.pdf', 'wb') as f:
f.write(img2pdf.convert(items))

かなり実行速度が速く、体感的には一瞬で変換してくれました。まだ、目次の移植や右綴じへの変換などは出来ていないので、追々検討しようと思います。

ChatGPTが、吐き出したプログラムが実際には動かないプログラムというケースもありますが、今回の事例では良い精度で実行したい内容を動くプログラムにしてくれたので驚いています。 今後は、知りたいことを検索エンジンで似たようなことをやっている記事を探すのではなく、チャットで正解例を教わるような時代になるのでしょうか?
今後の進展が気になります。

追記:2022/12/18
pikepdfで右綴じにする部分を追加しました。
import io
import pikepdf
import img2pdf
import ebooklib
from ebooklib import epub
# 1. EPUBを読み込む
book = epub.read_epub('input.epub')
# 2. EPUBの各ページを画像として取得する
items = []
for item in book.get_items():
if item.get_type() == ebooklib.ITEM_COVER:
items.append(item.get_content())
elif item.get_type() == ebooklib.ITEM_IMAGE:
items.append(item.get_content())
# 3. 画像をPDF形式に変換する
file_obj = io.BytesIO(img2pdf.convert(items))
# 4. PDFを右綴じ(R2L)にする
with pikepdf.Pdf.open(file_obj) as pdf:
pdf.Root.PageLayout = pikepdf.Name.TwoPageRight
pdf.Root.ViewerPreferences = pikepdf.Dictionary()
pdf.Root.ViewerPreferences.Direction = pikepdf.Name.R2L
pdf.save('output_R2L.pdf')

追記:2023/01/09
ePubからの目次を移植する部分を追加しました。
import io
import pikepdf
import img2pdf
import ebooklib
from ebooklib import epub, utils
from pikepdf import Pdf, OutlineItem
# 1. ePubを読み込む
book = epub.read_epub('input.epub')
# 2. ePubの各ページのファイル名と画像を取得する
page_names = []
page_items = []
for item in book.get_items():
if item.get_type() == ebooklib.ITEM_COVER:
page_names.append(item.get_name())
page_items.append(item.get_content())
elif item.get_type() == ebooklib.ITEM_IMAGE:
page_names.append(item.get_name())
page_items.append(item.get_content())
# 3. ePub内の画像をPDF形式に変換する
file_obj = io.BytesIO(img2pdf.convert(page_items))
# 4. ePubの目次情報を取得し、画像リストと照らし合わせる
index_numbers = []
for item in book.get_items_of_type(ebooklib.ITEM_DOCUMENT):
body = utils.parse_html_string(item.get_body_content())
for elem in body.iter():
if 'xlink:href' in elem.attrib:
for chapter in book.toc:
if item.get_name() == chapter.href:
image_href = elem.attrib.get('xlink:href')[3:]
index_number = page_names.index(image_href)
index_numbers.append(index_number)
elif 'src' in elem.attrib:
for chapter in book.toc:
if item.get_name() == chapter.href:
image_href = elem.attrib.get('src')[3:]
index_number = page_names.index(image_href)
index_numbers.append(index_number)
# 5. PDFに目次情報を書き込み、右綴じ(R2L)にする
with pikepdf.Pdf.open(file_obj) as pdf:
outitem = []
for chapter, index_number in zip(book.toc, index_numbers):
outitem.append(OutlineItem(chapter.title, index_number))
with pdf.open_outline() as outline:
outline.root.extend(outitem)
pdf.Root.PageLayout = pikepdf.Name.TwoPageRight
pdf.Root.ViewerPreferences = pikepdf.Dictionary()
pdf.Root.ViewerPreferences.Direction = pikepdf.Name.R2L
pdf.save('output_outline_R2L.pdf')
追追記:2023/02/24
Ebooklibを使わずに、BeautifulSoupを使うバージョンを作成しました。
コマンドライン実行を想定していて、綴じ方なども指定できるように変更しました。


今後は、逆に画像のみのPDFから固定レイアウトのePubへの変換プログラム作成に取り組みたいと思います。