PDF フォーム

LaTeX で PDF フォーム——記入できるテキスト欄やチェックボックス——を作るとき、最初に踏む地雷はいつも同じです。\TextField{Name} と書いてコンパイルすると、エラーも警告も出ないまま、入力欄がどこにもない PDF が出てきます。ラベルの「Name」だけが本文に組まれ、pdfinfoForm: none と答え、PDF の中を覗いても入力部品(Widget 注釈)は 1 個も見つかりません。hyperref のフォーム命令は Form 環境の外では静かに何も作らないからです。このページでは、\begin{Form} から始めて各フィールドが実際に PDF へ何を書き込むのかを実測で追い、送信ボタンが既定でどこへ何を送るのか、そしてどこで諦めて Web フォームにすべきかまでを扱います。

Form 環境がないとフィールドは作られない

対話的なフィールドはすべて \begin{Form} … \end{Form} の内側に置きます。これは書き方の作法ではなく、実際に出力が変わります。Form 環境の外に \TextField\CheckBox を置いた文書をコンパイルすると、pdflatex は終了コード 0 で正常終了し、警告も一切出しませんが、できあがった PDF に含まれる /Widget 注釈は 0 個pdfinfoForm: 欄も none のままです。同じ命令を Form 環境の中に移すと、pdfinfoForm: AcroForm と答えるようになります。PDF のフォームは AcroForm という 1 つの辞書が全フィールドを束ねる構造になっていて、Form 環境こそがその辞書を作る役だからです。なお \usepackage{hyperref} と書くだけで十分で、[pdftex] のようなドライバ指定は要りません——ログを見れば hpdftex.def が自動で読まれています。

Form 環境が取るオプションは、hyperref のソースを数えると 4 つだけ です——action(送信先)、methodencodingNeedAppearances。フィールド側の見た目や挙動は個々の命令の [...] で指定するので、環境のオプションは送信の設定だと思っておけば十分です。

latex
\documentclass{article}
\usepackage{hyperref}          % no driver option needed
\begin{document}
\begin{Form}[action={https://example.org/collect},method=post]
  \TextField[name=fullname,width=6cm]{Name}\par
  \CheckBox[name=agree]{I agree}\par
  \ChoiceMenu[combo,name=affil]{Affiliation}{University,Company,Other}\par
  \Submit{Send}\quad\Reset{Clear}
\end{Form}
\end{document}

フィールドの種類と、PDF に書かれるもの

フィールド命令は 5 つで、PDF 側では 3 種類のフィールド型に落ちます——テキストは /Tx、選択は /Ch、ボタン類(チェックボックス・押しボタン・送信・リセット)はすべて /Btn です。冒頭の例をコンパイルして中身を取り出すと、確かに /Tx が 2 個、/Ch が 1 個、/Btn が 4 個並んでいます。ボタンがひとまとめなのは PDF の仕様どおりで、チェックボックスと押しボタンの違いは型ではなくフラグ/Ff)のビットで表されます。この「フラグで性格が決まる」構造は、次の節で効いてきます。

命令PDF 上の型生成されるもの
\TextField/Txテキスト入力欄。multilinepasswordmaxlen で性格を変えられる
\CheckBox/Btnチェックボックス。既定値は /Offchecked で最初からオンにできる
\ChoiceMenu/Ch または /Btncombo は編集可能なドロップダウン、popdown はリストボックス、radio はラジオ群(/Btn になる)
\PushButton/Btn押しボタン。onclick= に JavaScript を書くと /S /JavaScript のアクションになる
\Submit / \Reset/Btn/S /SubmitForm/S /ResetFormフィールド名は常に SubmitReset で、引数は表示される文字だけ

name= を省くとラベルがフィールド名になる — ラジオ群の落とし穴

name= を書かないと、ラベルの文字列がそのままフィールド名になります。 \TextField{Your name} を組んだ PDF を覗くと、フィールド名は /T (Your name)——空白ごと入っています。データを受け取る側から見れば扱いにくい名前ですし、日本語のラベルを書けば日本語の名前になります。フィールド名は必ず name= で ASCII の識別子を明示するのが実務の作法です。もう一つ、同じ name= を 2 か所に書くと、PDF は同名のフィールドを同一のフィールドとみなします——name=dup\TextField を 2 つ置くと /T (dup) を持つオブジェクトが 2 つでき、一方に入力するともう一方も同じ値になります。同じ値を 2 か所に表示したいときは便利な性質ですが、うっかり名前が衝突すると原因の分かりにくい不具合になります。

そのラジオボタンには、もっと踏み込んだ問題があります。\ChoiceMenu[radio,name=r1]{Pick}{a,b,c} を組むと、r1 という同じ名前を持つ /Btn オブジェクトが 3 つ できます。ところが AcroForm 辞書の /Fields 配列に載るのは 最初の 1 つだけ。残り 2 つは、どのフィールドからも参照されないまま宙に浮きます。実際 qpdf にかけると WARNING: this widget annotation is not reachable from /AcroForm in the document catalog という警告が 2 回出ます。PDF の仕様では、ラジオ群は 1 つの親フィールドが /Kids で子を束ねる形が正しいのですが、hyperref は平らに並べます。表示されるビューアが多いので気づきにくいものの、厳密な PDF 処理系や自動抽出に通すと壊れうる構造です。選択肢が固定なら、ラジオより combopopdown のほうが素直です。

よく使うオプション — フラグになるものと、ならないもの

各フィールドは [...] に多数のオプションを取ります(hyperref が定義しているキーは 30 近くあります)。よく使うのは name=width=height=default=(初期値)、bordercolorbackgroundcolorcharsize(文字サイズ)、align(0=左・1=中央・2=右)、maxlen=(最大文字数)、menulength=(リストの表示行数)あたり。この中で multilinereadonlypassword の 3 つだけは値を取らないスイッチで、PDF のフラグに 1 対 1 で対応します——hyperref.sty の 5283 行以降に ReadOnly がビット 1、Multiline がビット 13、Password がビット 14 と定義されており、実際に組んで /Ff を読むと readonly は 1、multiline は 4096、password は 8192 になります。一方 maxlen=5 はフラグではなく /MaxLen 5 という独立した項目として書かれます。この区別が分かると、思ったとおりに効かないときにどこを見ればよいかが決まります。

latex
\begin{Form}
  \TextField[name=notes,multiline,width=8cm,height=3cm]{Notes}\par
  \TextField[name=locked,readonly,width=4cm,default={fixed}]{Locked}\par
  \TextField[name=short,maxlen=5,width=3cm]{Max 5}\par
  \TextField[name=email,width=5cm,align=0,
             bordercolor={0 0 0},backgroundcolor={1 1 0.9}]{Email}
\end{Form}

\Submit は既定で FDF を送る — method=post だけでは足りない

ここがこのページでいちばん重要な一点です。\begin{Form}[action={https://example.org/collect},method=post] と書いて送信ボタンを押しても、サーバに届くのは HTML フォームの POST ではなく FDF という Acrobat 独自形式のデータです。 原因は hyperref.sty の 5371 行、\def\Fld@export{fdf}——既定の書き出し形式が FDF に設定されているからです。実際にコンパイルして送信アクションを取り出すと /S /SubmitForm/Flags の項目自体が存在せず、全フラグが 0、つまり FDF になっています。method=post はどうしたのかというと、5378 行からの \HyField@FlagsSubmit を読めば分かります。method が立てる GetMethod フラグは HTML と PDF の分岐でしか使われず、FDF の分岐では完全に無視されます。 つまり method=post は単独では何の効果もありません。

普通の Web サーバで受けたいなら、Form 環境に encoding=html を足します。これは hyperref.sty の 5665 行あたりで \def\Fld@export{html} を実行する専用のキーで、これを付けて組み直すと送信アクションに /Flags 4 が現れます——ビット 3 の ExportFormat が立ち、HTML 形式になったということです。ちなみに encodinghtml 以外を書くと Form 'encoding' key with unknown value という警告が出るだけで、黙って無視されます。書き出し形式はほかに xfdf(XML 版の FDF)と pdf(記入済み PDF をまるごと送る)も選べます。

latex
% FDF (the default) -- your endpoint receives an Acrobat-specific blob
\begin{Form}[action={https://example.org/collect},method=post]

% an ordinary HTML form post -- note encoding=html
\begin{Form}[action={https://example.org/collect},encoding=html,method=post]

実際のビューアで何が起きるか、そしていつ諦めるか

hyperref が作るフォームは、入力欄の見た目そのものをファイルに書き込まず、AcroForm 辞書に /NeedAppearances true を立てて「描画はビューア側でやってくれ」と頼む方式です。Acrobat Reader はこの依頼に応えますが、対応の度合いはビューアによって差があり、ブラウザ内蔵の PDF 表示や軽量ビューアでは枠が出ない・入力できないといったことが起こります。\PushButton[onclick=...] の JavaScript にいたっては、対応しているビューアのほうが少数派です。同じ性質が別の場所にも波及します——/NeedAppearances に頼る構造は PDF/A に適合できません。 入力欄を 1 つ置いただけで veraPDF は clause 6.3.3「An annotation does not contain an appearance dictionary」で不合格を返します(PDF/A のページに詳しく書きました)。

ここまでを踏まえると、PDF フォームを選ぶ理由はかなり限られます。 検証やスクリプトなど込み入ったことをしたいなら insdljs や AcroTeX の eforms という選択肢はありますが、そこまで積み上げても「相手のビューアで動くか」は最後まで保証できません。回答をオンラインで集めたいだけなら、Web フォームのほうが確実で速いというのが正直な結論です。逆に PDF フォームが向くのは、紙で配る前提の様式で、受け取る側がパソコンで記入して印刷や PDF 保存をする——つまり送信機能を使わない場合です。この用途なら記入欄が入力可能なだけで十分に役立ちますし、readonly で固定した欄と組み合わせれば、テンプレートとして安定して使えます。