Rで解析:PDFからテキスト・座標・フォント情報を抽出できる「pdftools」パッケージの紹介
研究や実務では、PDF形式で配布された資料からテキストや数値を取り出し、集計や分析に使いたい場面があります。しかし、PDFは表示のためのレイアウト情報を持つ形式であり、そこから必要な文字情報だけを取り出すには手間がかかります。
「pdftools」パッケージは、Popplerライブラリを基盤として、PDFからページ単位のテキストや単語ごとの座標、埋め込みフォント、文書のメタデータを取り出すことが可能です。また、各ページを指定した解像度でビットマップ画像に描画したり、PNGやJPEGなどの画像ファイルへ変換したりすることも可能です。本パッケージの利用で、PDF資料を起点としたデータの抽出や画像化を、R上で一貫して進められるのではないかと考えます。
パッケージバージョンは3.9.1。Windows 11 x64 (build 26200)のR version 4.6.1で確認しています。
<おすすめのRに関する書籍です>
パッケージのインストール
下記コマンドを実行してください。
# パッケージのインストール
install.packages("pdftools")
# パッケージの読み込み
library("pdftools")
Using poppler version 26.01.0コマンド例
詳細はコメント、パッケージのヘルプを確認してください。
pdftoolsの関数は、大きくテキスト抽出系とレンダリング系に分けられます。テキスト抽出系のpdf_textはページ単位の文字列を、pdf_dataは単語ごとに位置と大きさを持つデータフレームを返します。pdf_infoは作成日やページ数などのメタデータ、pdf_fontsは埋め込みフォント、pdf_pagesizeは各ページの寸法を返します。レンダリング系のpdf_render_pageはページをビットマップとしてメモリ上に描画し、pdf_convertはページを画像ファイルとして書き出します。
以下では、京都府の宇治茶の産地別生産量をまとめた擬似データを、日本語を含む2ページのPDFとして書き出し、そのPDFを題材に各関数の動作を確認します。
# 作業用の一時ファイルとして出力するPDFのパス
ocha_pdf <- file.path(tempdir(), "uji_ocha_report.pdf")
# 京都府の宇治茶産地に関する擬似データ
sanchi <- data.frame(
産地 = c("宇治市", "和束町", "宇治田原町", "南山城村", "京田辺市"),
荒茶生産量 = c(418, 605, 352, 274, 191),
主力品種 = c("やぶきた", "ごこう", "さみどり", "おくみどり", "うじひかり")
)
# 1ページ目に産地一覧、2ページ目に棒グラフを描いたPDFを書き出す
cairo_pdf(ocha_pdf, width = 7, height = 5, onefile = TRUE)
plot.new()
title(main = "宇治茶 産地別生産量レポート(擬似データ)")
# 産地ごとの生産量と主力品種を1行ずつ書き込む
text(0, seq(0.9, 0.5, length.out = nrow(sanchi)),
adj = 0,
labels = paste0(sanchi$産地, ":", sanchi$荒茶生産量, "トン(", sanchi$主力品種, ")")
)
# 2ページ目として棒グラフを描く
barplot(sanchi$荒茶生産量,
names.arg = sanchi$産地,
main = "産地別の荒茶生産量", ylab = "トン"
)
dev.off()
png
2PDFのメタデータを取得する:pdf_infoコマンド
作成日時、更新日時、ページ数、PDFのバージョン、作成に使われたソフト名などの文書情報をリストとして返します。ページ数だけを使いたい場合は結果のpages要素を参照します。
| オプション | 意味 | 初期値 |
|---|---|---|
| PDFファイルのパス、またはPDFデータのrawベクトル | なし | |
| opw | PDFを開くためのオーナーパスワード(文字列) | “” |
| upw | PDFを開くためのユーザーパスワード(文字列) | “” |
# PDFのメタデータ(作成日・ページ数・作成ソフトなど)を取得する
joho <- pdf_info(ocha_pdf)
# ページ数だけを取り出す
joho$pages
[1] 2
# 文書全体の情報を表示する
joho
$version
[1] "1.7"
$pages
[1] 2
$encrypted
[1] FALSE
$linearized
[1] FALSE
$keys
$keys$Producer
[1] "cairo 1.18.4 (https://cairographics.org)"
$created
[1] "2026-09-04 12:24:57 JST"
$modified
[1] "1970-01-01 08:59:59 JST"
$metadata
[1] ""
$locked
[1] FALSE
$attachments
### 出力は省略(残り4行)ページ単位でテキストを抽出する:pdf_textコマンド
各ページのテキストを1要素とする文字列ベクトルを返します。既定では紙面のレイアウト順に並べ替えて返し、raw = TRUEを指定するとPDF内部のストリーム順のまま取り出します。
| オプション | 意味 | 初期値 |
|---|---|---|
| PDFファイルのパス、またはPDFデータのrawベクトル | なし | |
| opw | PDFを開くためのオーナーパスワード(文字列) | “” |
| upw | PDFを開くためのユーザーパスワード(文字列) | “” |
| raw | 物理的なレイアウト順ではなくrawストリーム順でテキストを返す | FALSE |
# ページごとの文字列ベクトルとしてテキストを抽出する
honbun <- pdf_text(ocha_pdf)
# 1ページ目のテキストを表示する
cat(honbun[1])
宇治茶 産地別生産量レポート(擬似データ)
宇治市:418トン(やぶきた)
和束町:605トン(ごこう)
宇治田原町:352トン(さみどり)
南山城村:274トン(おくみどり)
京田辺市:191トン(うじひかり)
# 内部のストリーム順のまま取り出す
honbun_raw <- pdf_text(ocha_pdf, raw = TRUE)
<おすすめのRに関する書籍です>
単語ごとの座標付きでテキストを取り出す:pdf_dataコマンド
ページごとに、単語とその位置(x、y)、幅、高さ、書字方向などを列に持つデータフレームのリストを返します。表のセルや見出しなど、紙面上の位置を手がかりに抽出したいときに使います。font_info = TRUEを指定すると各単語のフォント情報も付きますが、新しいPopplerが必要です。
| オプション | 意味 | 初期値 |
|---|---|---|
| PDFファイルのパス、またはPDFデータのrawベクトル | なし | |
| font_info | TRUEのとき各ボックスのフォント情報も抽出する(新しいPopplerが必要で、なければエラーになる) | FALSE |
| opw | PDFを開くためのオーナーパスワード(文字列) | “” |
| upw | PDFを開くためのユーザーパスワード(文字列) | “” |
# 単語ごとに位置・大きさを持つデータフレームのリストとして抽出する
moji <- pdf_data(ocha_pdf)
# 1ページ目の先頭部分を確認する
head(moji[[1]])
width height x y space text
1 32 16 156 21 TRUE 宇治茶
2 183 16 192 21 FALSE 産地別生産量レポート(擬似データ)
3 128 13 74 81 FALSE 宇治市:418トン(やぶきた)
4 119 13 74 102 FALSE 和束町:605トン(ごこう)
5 146 13 74 124 FALSE 宇治田原町:352トン(さみどり)
6 146 13 74 145 FALSE 南山城村:274トン(おくみどり)
# 各単語のフォント情報も付けて抽出する
moji_font <- pdf_data(ocha_pdf, font_info = TRUE)埋め込みフォントを一覧する:pdf_fontsコマンド
PDFで使われているフォント名、種類、埋め込みの有無などをデータフレームで返します。フォントが埋め込まれていない場合、環境によって表示や抽出結果が変わることがあるため、その確認に使えます。
| オプション | 意味 | 初期値 |
|---|---|---|
| PDFファイルのパス、またはPDFデータのrawベクトル | なし | |
| opw | PDFを開くためのオーナーパスワード(文字列) | “” |
| upw | PDFを開くためのユーザーパスワード(文字列) | “” |
# PDFに埋め込まれたフォントの一覧を取得する
font_ichiran <- pdf_fonts(ocha_pdf)
# フォント名と埋め込みの有無を表示する
font_ichiran
name type embedded file
1 BICLDM+Arial-BoldMT cid_truetype TRUE
2 KMJXUC+Arial-BoldMT truetype TRUE
3 HCWRAC+ArialMT cid_truetype TRUE
4 RJAMDI+ArialMT truetype TRUEページの寸法を取得する:pdf_pagesizeコマンド
各ページの幅と高さをポイント単位(1ポイント=1/72インチ)でデータフレームとして返します。用紙サイズの確認や、pdf_dataで得た座標を割合に直すときの基準として使えます。
| オプション | 意味 | 初期値 |
|---|---|---|
| PDFファイルのパス、またはPDFデータのrawベクトル | なし | |
| opw | PDFを開くためのオーナーパスワード(文字列) | “” |
| upw | PDFを開くためのユーザーパスワード(文字列) | “” |
# 各ページの幅・高さ(ポイント単位)を取得する
size <- pdf_pagesize(ocha_pdf)
# ページごとの寸法を表示する
size
top right bottom left width height
1 0 504 360 0 504 360
2 0 504 360 0 504 360
<おすすめのRに関する書籍です>
ページをビットマップ画像に描画する:pdf_render_pageコマンド
指定したページを指定解像度で描画し、画像データをrawベクトル(またはnumeric = TRUEで0〜1の実数配列)としてメモリ上に返します。pngパッケージと組み合わせると、任意のページをPNGとして保存できます。
| オプション | 意味 | 初期値 |
|---|---|---|
| PDFファイルのパス、またはPDFデータのrawベクトル | なし | |
| page | 描画するページ番号 | 1 |
| dpi | 描画時の解像度(1インチあたりのドット数) | 72 |
| numeric | raw出力を0〜1の実数値に変換する | FALSE |
| antialias | アンチエイリアスを有効にする(”text”、”draw”、TRUE、FALSEのいずれか) | TRUE |
| opw | PDFを開くためのオーナーパスワード(文字列) | “” |
| upw | PDFを開くためのユーザーパスワード(文字列) | “” |
# 2ページ目を150dpiのビットマップとして描画する
bitmap <- pdf_render_page(ocha_pdf, page = 2, dpi = 150)
# 画像の次元(チャンネル数・幅・高さ)を確認する
dim(bitmap)
[1] 4 1050 750
# pngパッケージでPNGファイルとして保存する
png::writePNG(bitmap, file.path(tempdir(), "ocha_page2.png"))ページを画像ファイルに変換する:pdf_convertコマンド
指定したページをPNGやJPEGなどの画像ファイルとして書き出し、生成したファイルのパスを返します。pagesで対象ページを、filenamesで出力先を指定します。利用できる形式はpoppler_config()$supported_image_formatsで確認できます。
| オプション | 意味 | 初期値 |
|---|---|---|
| PDFファイルのパス、またはPDFデータのrawベクトル | なし | |
| format | 出力形式を表す文字列(”png”や”jpeg”など。poppler_config()$supported_image_formatsのいずれかと一致する必要がある) | “png” |
| pages | 描画するページ番号のベクトル(1始まり)。NULLは全ページ | NULL |
| filenames | pagesと同じ長さの出力ファイル名ベクトル。pagesとformatで展開される書式文字列でもよい | NULL |
| dpi | 描画時の解像度(1インチあたりのドット数) | 72 |
| antialias | アンチエイリアスを有効にする(”text”、”draw”、TRUE、FALSEのいずれか) | TRUE |
| opw | PDFを開くためのオーナーパスワード(文字列) | “” |
| upw | PDFを開くためのユーザーパスワード(文字列) | “” |
| verbose | 進捗情報を標準出力に表示する | TRUE |
# 出力先の画像ファイル名を2ページ分用意する
shutsuryoku <- file.path(tempdir(), c("ocha_p1.png", "ocha_p2.png"))
# 1〜2ページ目をそれぞれPNG画像ファイルに変換する
gazou <- pdf_convert(ocha_pdf,
format = "png", pages = 1:2,
filenames = shutsuryoku, dpi = 150
)
Converting page 1 to C:\Users\chalu\AppData\Local\Temp\RtmpmsbcKz/ocha_p1.png... done!
Converting page 2 to C:\Users\chalu\AppData\Local\Temp\RtmpmsbcKz/ocha_p2.png... done!
# 生成された画像ファイルのパスを表示する
gazou
[1] "C:\\Users\\chalu\\AppData\\Local\\Temp\\RtmpmsbcKz/ocha_p1.png"
[2] "C:\\Users\\chalu\\AppData\\Local\\Temp\\RtmpmsbcKz/ocha_p2.png"
<おすすめのRに関する書籍です>
この記事が誰かの役に立ちますように。