第5回目 の記事ではHTMLフォームからのデータの扱いについて学びました。フォームからPOSTで送信されたデータはFormスコープに、GETで送られたデータはUrlスコープに代入されることが分かりました。
それと併せて、あるスコープに変数が定義されているかどうかをStructKeyExists()関数 で調べる方法を学びました。IsDefined()関数 でも同様のことが可能ですが、IsDefined()関数は参照可能なスコープを全て辿っていってしまうため、予期しない値を返すことを学びました。
今回はAdobe製品群の一員となったことで強化されたPDF機能を学びます。
HTMLをPDFに変換する
ColdFusion MX 7からの機能ですが、cfdocumentタグ を使用することで、cfdocumentタグで囲った中身のHTMLをPDFに変換することができます。簡単な例では以下のようなものになります。{cf_root}フォルダ({cf_root}はColdFusionをインストールしたディレクトリです。Windowsでは『C:\ColdFusion8』になります)内にある『wwwroot』の中に『gihyo』という名前のフォルダを作り、その中に『6』というフォルダを作ってください。『 6』のフォルダの中に以下のような中身のファイルをcfdocument.cfmとして保存してください(ファイルのパスは{cf_root}/wwwroot/gihyo/6/cfdocument.cfmになります) 。なお保存時の文字コードはUTF-8としてください。その後、http://127.0.0.1:8500/gihyo/6/cfdocument.cfmでアクセスしてみましょう。
<cfprocessingdirective pageEncoding="UTF-8" />
<cfdocument format="PDF">
<style>
h1{
border-left: 5px solid #067499;
border-bottom: 1px solid #067499;
padding-left: 0.5em;
font-size: 1.5em;
}
</style>
<h1>ColdFusion-開発効率を求められる今だから知りたい高性能Webアプリケーションサーバー</h1>
<p>2007年9月21日に最新版ColdFusion 8日本語版がアドビシステムズ株式会社よりリリースされました。この連載では、なぜColdFusionが長きに渡ってWeb開発者から支持を集めてきたのか、その魅力に迫ります。</p>
</cfdocument>
図1 cfdocument.cfmの実行結果
Adobe Readerなどのインストールが必要ですが、ご覧の通りcfdocumentタグで囲った中身がPDF化されて表示されていることが分かります。
また、cfdocumentタグの中身にはCFMLを含ませることができます。例えば、第4回の例で使ったcfchartタグ でグラフを描画し、それをPDF化してみましょう。なお、cfchartタグをcfdocumentタグの中に入れることができるのはColdFusion 8からの機能になります。
以下の例をchartpdf.cfmとして保存し、アクセスすると、PDF化されたグラフがブラウザ上で表示またはダウンロードされるでしょう。
<cfprocessingdirective pageEncoding="UTF-8" />
<cfquery datasource="cfartgallery" name="qry">
SELECT
COUNT(art.artID) AS num, art.mediaID
FROM
art
WHERE
isSold=0
GROUP BY
mediaID
</cfquery>
<cfdocument format="PDF" name="aPdf">
<cfchart format="png" chartheight="200" chartwidth="440" font="arialunicodeMS" xAxisTitle="メディアID " yAxisTitle="個">
<cfchartseries type="bar" query="qry" itemColumn="mediaID" valueColumn="num" seriesLabel="在庫" ></cfchartseries>
</cfchart>
</cfdocument>
<cfcontent type="application/pdf" variable="#aPdf#">
図2 chartpdf.cfmの実行結果
この例ではPDFのバイナリデータを「aPdf」と言う名前の変数に格納し、そのデータをcfcontentタグ のvariable属性を使い直接ブラウザに送出しています。MIME media typeとしては「application/pdf」を指定しているので、お使いのブラウザ環境によって、ブラウザ内で開かれたり、ダウンロードダイアログが出るなどの動作になると思います。
このようにCFMLを組み合わせてクエリーを元にグラフなどを描画し、それをPDF化することも可能なので、決算短信や営業成績レポートなどの生成に使うこともできるでしょう。
cfpdfタグ
ColdFusion 8から、PDF操作専用のCFMLタグcfpdfタグ が追加されました。cfpdfタグでは以下のようなことができます。
透かしの追加・除去
PDFの情報の取得
2つ以上のPDFファイルのマージ
PDFファイルのパスワードでの保護及び暗号化
PDFの各ページのサムネイルの作成
DDX(Document Description XML)を用いたLiveCycle Assemblerによる高度なPDF処理
PDFを暗号化する
cfpdfタグを使って、送信されてきたPDFファイルを暗号化してみることにしましょう。以下の例のpdfenc.cfmとして保存し、適当なPDF(ColdFusion 8パフォーマンス評価ガイド など)をダウンロードした後、pdfenc.cfmにアクセスし、ダウンロードしたPDFとパスワードを入力し、「 暗号化してダウンロード」ボタンを押してみましょう。ファイルダウンロードダイアログが表示されると思います。そして、ダウンロードしたPDFを開くには、先ほどフォームに入力したパスワードの入力が求められると思います。
<cfprocessingdirective pageEncoding="UTF-8" />
<cfif StructKeyExists(Form, 'submit')>
<cffile action="upload" filefield="pdf" destination="#GetTempDirectory()#" nameconflict="MakeUnique">
<cfpdf action="protect" newUserPassword="#Form.password#" source="#cffile.serverDirectory#/#cffile.serverFile#" name="aPdf">
<cfset aDownloadName = ReplaceNoCase(cffile.serverFile, '.pdf', '_enc.pdf')>
<cfheader name="Content-Disposition" value="attachment; filename=#aDownloadName#">
<cfcontent type="application/octet-stream" variable="#ToBinary(aPdf)#" reset="false">
</cfif>
<cfcontent type="text/html; charset=UTF-8">
<form action="pdfenc.cfm" method="post" enctype="multipart/form-data">
<p>PDFファイルを指定してください:<input type="file" name="pdf"><br>
パスワードを入力してください:<input type="password" name="password" value=""><br>
<input type="submit" name="submit" value="暗号化してダウンロード" ></p>
</form>
いくつか見慣れないタグやコードが出てきているので解説します。
cffileタグ ではHTMLフォームからアップロードされたファイルを実際にサーバー上にアップロードする作業をします。action属性に「upload」を指定し、フォームからのデータをアップロードする処理であることを明示します。次にfilefield属性にフォームのtype="file"に付けた名前(今回は「pdf」 )を指定し、どのフィールド名のファイルを扱うかを指定します。最後にdestination属性に、サーバーのどのディレクトリにファイルを置くかを指定します。今回はGetTempDirectory()関数 でColdFusionの一時ファイルを置くディレクトリを指定しました。nameconflict="MakeUnique"となっているのは、destination属性で指定したディレクトリに同名のファイルがアップロードされた場合、サーバー上で上書きされないようにするための処置です。
cffileタグでファイルをアップロードすると、cffileスコープというスコープが生成されます。cffileスコープにはserverDirectory(アップロードしたファイルがあるディレクトリ。つまり先ほどdestination属性属性で設定したディレクトリ)やserverFile(アップロードしたファイルのファイル名)などが格納されています。それ以外の値についてはcffileスコープをcfdumpしてみてください(または「cffile action = "upload" 」のリファレンスページを参照してください) 。
次に、cfpdf action="protect"とし、source属性で指定したPDFファイルにnewUserPassword属性で指定したパスワードをかける処理をしています。パスワード処理したPDFファイルはname属性で指定した変数に格納されます。
ReplaceNoCase()関数 は、その名前の通り大文字小文字を区別せず文字列を置換する関数で、ブラウザにダウンロードさせるファイル名の末尾が「_enc.pdf」になるように、元のアップロードファイル名からダウンロードファイル名を作り出しています。
cfheaderタグ はHTTPレスポンスヘッダを指定するためのタグです。今回は先ほどReplaceNoCase()関数で作り出したファイル名でファイルを保存させたいため、HTTPレスポンスヘッダにContent-Dispositionを追加しています。
最後にcfcontentタグ で、MIME media typeが「application/octet-stream」なデータをこれから送ることを宣言し、データの中身をvariable属性に渡しています。
オーダーメイドPDFを作る
cfpdfタグではPDF中の任意のページを抽出して、それらを結合し1枚のPDFにすることができます。この機能を利用してオーダーメイドPDFを作成することにしましょう。オーダーメイドPDFとは、あるPDFからユーザーが欲しいと思ったページのみを抽出し、1ファイルにまとめたPDFの事です。
まず、適当なPDFファイル(ColdFusion 8パフォーマンス評価ガイド など)を{cf_root}/wwwroot/gihyo/6/source.pdfとして保存します。その後、以下のCFMLをordermade.cfmとして保存し、アクセスします。
<cfprocessingdirective pageEncoding="UTF-8" />
<!--- PDFのサムネイルを置くディレクトリなど --->
<cfset aCurrentDir = GetDirectoryFromPath(GetTemplatePath())>
<cfset aThumbsUrl = 'thumbs'>
<cfset aThumbsDir = aCurrentDir & aThumbsUrl>
<cfset aSourcePdf = aCurrentDir & 'source.pdf'>
<cfif NOT DirectoryExists(aThumbsDir)>
<!--- PDFのサムネイルが作成されていないので作成する --->
<cfdirectory
action="create"
directory="#aThumbsDir#">
<cfpdf
action="thumbnail"
source="#aSourcePdf#"
destination="#aThumbsDir#"
format="png"
scale="50"
imagePrefix="thumbs"
overwrite="yes">
</cfif>
<!--- オーダーメイドPDFを作る部分 --->
<cfif StructKeyExists(Form, 'submit')>
<cfpdf action="merge" source="#aSourcePdf#" pages="#Form.page#" name="aPdf">
<cfheader name="Content-Disposition" value="attachment; filename=catalog.pdf">
<cfcontent type="application/octet-stream" variable="#ToBinary(aPdf)#" reset="false">
</cfif>
<!--- オーダーメイド用の情報を指定してもらう部分 --->
<cfcontent type="text/html; charset=UTF-8">
<cfpdf action="getInfo" source="#aSourcePdf#" name="aPDFInfo">
<form action="ordermade.cfm" method="post">
<p>
<cfoutput>
<ul>
<cfloop from="1" to="#aPDFInfo.TotalPages#" index="i">
<li><input type="checkbox" name="page" value="#i#">ページ#i#
<img src="#aThumbsUrl#/thumbs_page_#i#.png" height="100" style="vertical-align:middle;"></li>
</cfloop>
</ul>
</cfoutput>
<input type="submit" name="submit" value="オーダーメイドする" >
</p>
</form>
すると、source.pdfとして保存したPDFの各ページのサムネイルが表示され、左横にチェックボックスが現れたと思います。欲しいページにチェックボックスを入れ、「 オーダーメイドする」ボタンを押すと、チェックを入れたページのみが書き込まれたPDFが生成され、ダウンロードダイアログが表示されるかと思います。
図3 ordermade.cfmの実行結果
このように顧客一人一人に合わせたPDFをサーバー上で作成することも可能なので、オーダーメイドのカタログを作成することも可能でしょう。
また、今回は紹介しませんでしたが、cfprintタグ と組み合わせると、ColdFusionが動いているサーバーに接続されたプリンターからPDFを印刷することも可能です。顧客が選んだページのみをプリンタで出力し、顧客ごとにカスタマイズされたダイレクトメールを郵送するなどの使い方も考えられるでしょう。
まとめと次回予告
今回はAdobe製品の一員となったことで強化されたPDF機能をご紹介しました。
特にColdFusion 8で追加されたcfpdfタグでは、複雑なPDF操作がカプセル化されており、オーダーメイドPDFのような複雑な処理も40行程度で記述できてしまうことが分かりました。
今回はご紹介していませんが、DDXというLiveCycleで用いるPDF処理言語を用いると、PDFにヘッダを追加するなど、LiveCycleで行うような複雑なPDF操作をColdFusion上で実現することもできます。
帳票文化が色濃く残る日本において、PDFがタグ1つで簡便に扱えるColdFusionのメリットは計り知れないでしょう。
次回はラインデバッギングをご紹介します。