Python, pypdfでPDFの作成者やタイトルなどを取得・削除・変更
Pythonのサードパーティライブラリpypdf(旧PyPDF2)を使うと、PDFファイルのメタデータ(作成者、タイトルなど)の取得や削除、変更ができる。
サンプルで使用しているPDFファイルは以下のリンクから。暗号化されているファイルのパスワードはすべてpassword
。
すべてのPDFファイルに対して動作を保証するものではない。
pypdfのインストール
pypdfは外部ライブラリに依存しておらず、pip
(pip3
)でインストール可能。AES方式の暗号化・復号を利用する場合はpypdf[crypto]
とする。
$ pip install pypdf
$ pip install pypdf[crypto]
以下のサンプルコードで使用しているpypdfのバージョンは5.5.0
。
以前はPyPDF2という名前だったが、2023年にpypdfに改められた。
PDFファイルのメタデータの項目
PDFファイルの規格はISOで標準化されている。
ISO 32000-1 (PDF 1.7)は無料で公開されており、メタデータについては下記ファイルの「14.3 Metadata」(P548)で説明されている。
メタデータはMetadata Streams
かDocument Information Dictionary
に格納される。Document Information Dictionary
のメタデータの項目は以下の通り。
Title
: タイトルAuthor
: 作成者Subject
: サブジェクト(主題)Keyword
: キーワードCreator
: オリジナル文書の作成ツールProducer
: 変換ツールCreateDate
: 作成日時ModDate
: 更新日時Trapped
: トラッピングされているか
これらの項目は必須ではなく、また、その他の独自の項目を追加することも可能。
Author
, Creator
, Producer
は似たような項目名だが、それぞれ意味が異なる。Author
には文書を作成した個人や団体の名前が入り、Creator
やProducer
にはPDFファイルの作成・変換に使用されたソフトの名前が入る。
なお、2017年7月に公開されたISO 32000-2 (PDF 2.0)でメタデータに関する仕様が変更され、メタデータはExtensible Metadata Platform
(XMP
)に格納するようになった。
以下のサンプルコードではPDF 1.7以前の仕様に対する処理をメインで説明する。PDF 2.0に対する処理は最後に少しだけ述べる。
PDFファイルのメタデータの取得
PdfReader
クラスのmetadata
属性を使うと、PDFファイルのDocument Information Dictionary
からメタデータを取得できる。
PdfReader
はコンストラクタにPDFファイルのパスを指定して生成する。metadata
属性で取得できるのはDocumentInformation
クラスのオブジェクト。
title
やauthor
などを属性として取得できる。
import pypdf
print(pypdf.__version__)
# 5.5.0
pdf = pypdf.PdfReader('data/src/pdf/sample1.pdf')
print(type(pdf.metadata))
# <class 'pypdf._doc_common.DocumentInformation'>
print(pdf.metadata.title)
# sample1
pypdf.DocumentInformation
クラスは辞書(dict
)のサブクラスなので、[キー名]
で値を取得したりitems()
などのメソッドを使ったりすることも可能。
ファイルによっては、そのままprint()
で出力すると値がIndirectObject(...)
となる場合があるが、キーを指定すると中身が確認できる。なお、このサンプルファイルはMacのKeynoteで作成、PDFに変換したもの。
print(isinstance(pdf.metadata, dict))
# True
print(pdf.metadata)
# {'/Title': IndirectObject(33, 0, 4424533392), '/Producer': IndirectObject(34, 0, 4424533392), '/Creator': IndirectObject(35, 0, 4424533392), '/CreationDate': IndirectObject(36, 0, 4424533392), '/ModDate': IndirectObject(36, 0, 4424533392)}
print(pdf.metadata['/Title'])
# sample1
for k, v in pdf.metadata.items():
print(f'{k}: {v}')
# /Title: sample1
# /Producer: macOS バージョン10.14.2(ビルド18C54) Quartz PDFContext
# /Creator: Keynote
# /CreationDate: D:20190114072947Z00'00'
# /ModDate: D:20190114072947Z00'00'
キー名は'/Title'
のようにスラッシュ/
が付き、最初が大文字なので注意。
PDFファイルのメタデータを削除
メタデータをすべて削除
以下の手順でメタデータをすべて削除して保存できる。
- 元のPDFファイルから
PdfReader
オブジェクトを生成 - 中身をコピーした
PdfWriter
オブジェクトを生成(clone_from
引数を使用) metadata
属性にNone
を代入PdfWriter
オブジェクトをPDFファイルとして保存
src_pdf = pypdf.PdfReader('data/src/pdf/sample1.pdf')
dst_pdf = pypdf.PdfWriter(clone_from=src_pdf)
dst_pdf.metadata = None
dst_pdf.write('data/temp/sample1_no_meta.pdf')
メタデータを選択して削除
メタデータの項目を選択して削除したい場合は、元のPDFファイルのmetadata
属性をコピーして任意の項目を削除してから、保存するPDFファイルのmetadata
属性に代入する。
上述のようにmetadata
属性のpypdf.DocumentInformation
クラスは辞書(dict
)のサブクラスなので、dict()
で通常の辞書に変換すれば、pop()
やdel
などの標準的な辞書操作が安全に行える。例として/Creator
, /Producer
を削除する。
src_pdf = pypdf.PdfReader('data/src/pdf/sample1.pdf')
dst_pdf = pypdf.PdfWriter(clone_from=src_pdf)
metadata = dict(src_pdf.metadata)
print(metadata.keys())
# dict_keys(['/Title', '/Producer', '/Creator', '/CreationDate', '/ModDate'])
metadata.pop('/Creator')
del metadata['/Producer']
print(metadata.keys())
# dict_keys(['/Title', '/CreationDate', '/ModDate'])
これをPdfWriter
オブジェクトのmetadata
属性に代入し、write()
メソッドでPDFファイルとして保存する。
dst_pdf.metadata = metadata
dst_pdf.write('data/temp/sample1_remove_meta.pdf')
PDFファイルのメタデータを追加・変更
以下の手順でメタデータの項目を追加・変更できる。
- 元のPDFファイルから
PdfReader
オブジェクトを生成 - 中身をコピーした
PdfWriter
オブジェクトを生成(clone_from
引数を使用) add_metadata()
メソッドで任意の項目を辞書形式で指定PdfWriter
オブジェクトをPDFファイルとして保存
add_metadata()
メソッドには、設定項目とその値を辞書形式で指定する。add_metadata()
は既存の値を上書きするので、指定していない項目の値はそのまま。
src_pdf = pypdf.PdfReader('data/src/pdf/sample1.pdf')
dst_pdf = pypdf.PdfWriter(clone_from=src_pdf)
new_metadata = {
'/Title': 'new title',
'/Producer': 'new producer',
'/NewItem': 'special data'
}
dst_pdf.add_metadata(new_metadata)
dst_pdf.write('data/temp/sample1_new_meta.pdf')
print(pypdf.PdfReader('data/temp/sample1_new_meta.pdf').metadata)
# {'/Title': 'new title', '/Producer': 'new producer', '/Creator': IndirectObject(35, 0, 4398476304), '/CreationDate': IndirectObject(36, 0, 4398476304), '/ModDate': IndirectObject(36, 0, 4398476304), '/NewItem': 'special data'}
指定していない項目を削除したい場合は、辞書をmetadata
属性に代入して置き換えればよい。
dst_pdf.metadata = new_metadata
dst_pdf.write('data/temp/sample1_new_meta_replace.pdf')
print(pypdf.PdfReader('data/temp/sample1_new_meta_replace.pdf').metadata)
# {'/Title': 'new title', '/Producer': 'new producer', '/NewItem': 'special data'}
パスワードが設定されたPDFファイルの処理
パスワード付きの暗号化されたPDFファイルの場合、これまでのサンプルコードではエラーとなる。
PdfReader
オブジェクトを生成したあとでdecrypt()
メソッドを使って復号する必要がある。
src_pdf = pypdf.PdfReader(src_path)
src_pdf.decrypt('password')
また、保存するPDFファイルにパスワードを掛ける場合はPdfWriter
オブジェクトのwrite()
で保存する前にencrypt()
メソッドを使う。
dst_pdf.encrypt('password')
dst_pdf.write(dst_path)
パスワードについての詳細は以下の記事を参照。
XMPによるメタデータを取得(PDF 2.0)
上述のように、2017年7月に公開されたISO 32000-2 (PDF 2.0)でメタデータに関する仕様が変更され、メタデータはExtensible Metadata Platform
(XMP
)に格納するようになった。
以下のレポジトリにあるPDF 2.0のサンプルファイルを使って、XMP
で格納されたデータを取得する例を紹介する。
- pdf-association/pdf20examples: PDF 2.0 example files
- https://github.com/pdf-association/pdf20examples/raw/master/Simple%20PDF%202.0%20file.pdf
metadata
属性はNone
。
pdf = pypdf.PdfReader('data/temp/Simple PDF 2.0 file.pdf')
print(pdf.metadata)
# None
XMP
のデータはxmp_metadata
属性で取得できる。xmp_metadata
属性はXmpInformation
クラス。詳細は以下のドキュメント参照。
xmp_metadata
属性から各種の情報を取得できる。
print(type(pdf.xmp_metadata))
# <class 'pypdf.xmp.XmpInformation'>
print(pdf.xmp_metadata.dc_title)
# {'x-default': 'A simple PDF 2.0 example file'}
print(pdf.xmp_metadata.pdf_keywords)
# PDF 2.0 sample example
print(pdf.xmp_metadata.xmp_metadata_date)
# 2017-07-11 07:55:11
PdfWriter
クラスのドキュメントを読む限りadd_metadata()
に相当するようなメソッドはないので、新たなXMP
を追加することは出来ない模様(バージョン5.5.0
時点)。