Rails からの印刷出力

Rails からの印刷を考えます。Rails に限らず、web システムからの印刷は結構厄介そうです。

そこで、まずはPDFへの出力を検討します。

調べてみると、ruport なるものがありました。
インストールは簡単で、
gem install ruport
gem install ruport-util
で可能です。ruport は、pdf-writer を利用するらしい。
ActiveRecord から出力するには、
class User < ActiveRecord::Base
 acts_as_reportable
end

class RuportController < ApplicationController
 def userlist
  send_data User.report_table.to_pdf,
   :type => "application/pdf",
   :filename=> 'userlist.pdf',
   :disposition => 'inline'
 end
end
こんな感じになります。すこぶる簡単です。が、PDFお決まりの落とし穴が。
漢字が出ません。2バイト文字が扱えないのです。これはそもそも pdf-writer が対応していないかららしく、UTF-8 対応も、pdf-writer の対応待ちらしいです。
さて、漢字なしならOKかというと、このままではチト問題が。項目の出力順序が意図したとおりに並ばないのです。select とか、only とか付けてもダメで、しばし悩む。
解決策は次の通りです。
reorder('Field1', 'Field2', 'Field3')
みたいな感じで、順序の指定が可能です。判ってしまえばこれも簡単です。

と、ここまで検証したところで、PDF には見切りを付けました。フリーソフトで日本語を扱うのが難しそうだからです。

次に、PXDoc による出力を検証します。

PXDoc は、xml ベースのテキストを成形印刷する仕組みです。テキストファイルなので出力の作成は柔軟に出来そうですが、クライアントに専用ソフトのインストールが必要になります。PDF でも出力に専用ソフトは必要ですが、PXDoc は PDF のリーダーほどメジャーでないのが欠点でしょうか。

Rails からPXD形式で出力するには、例えば sample_controller.rb に、
def report
 @samples = Sample.find(:all)
 @response.headers["Content-Type"] = 'application/pxd'
 @response.headers["Content-Disposition"] = 'inline;filename="report.pxd"'
 render(:layout => false)
end
の様にしておきます。対応する view は、
.rhtml ではなく、report.rxml とし、
xm = Builder::XmlMarkup.new
xml << '<?xml version="1.0" encoding="utf-8"?>'

xml.tag!("pxd", "paper-type" => "A4", "orientation" => "landscape", "title" => "出力タイトル", "xmlns:xlink" => "http://www.w3.org/1999/xlink"){
 xml.tag!("page"){
  xml.tag!("svg", "width" => "29.7cm", "height" => "21cm", "viewBox" => "0 0 2970 2100"){
   xml.tag!("style", "type" => "text/css"){
    xml.cdata "\
    .col1 { x:200; dy:100;}\
    .col2 { x:400;}\
    .col3 { x:600; text-anchor:end;}\
   }

   xml.tag!("text", "y" => "500", "font-size" => "40"){

    for sample in @samples
     xml.tag!("tspan", "class" => "col1"){
      xml.text! sample.field1
     }
     xml.tag!("tspan", "class" => "col2"){
      xml.text! sample.field2
     }
     xml.tag!("tspan", "class" => "col3"){
      xml.text! sample.field3
     }
    end # for loop
    } # text
  } # svg
 } # page
}# pxd
の様にして出来ました。
xml << '<?xml version="1.0" encoding="utf-8"?>' は、xm.instruct! で行けるはずなんですが、うまく出力されず、生のステートメントを書いています。もっとも、どっちにしろテスト環境ではうまくレイアウトされますので、問題は無いんですが。
.rxml テンプレートファイルには、Builder::XmlMarkupが用いられるのですが、これがどう便利なんだか、私にはぜんぜん判っていません。
上記のようにした場合、ページのオーバーフローは無視されます。そこで、出力した行数を数えて、基準を超えたら改ページをさせたいところですが、XmlMarkup でどう実現するのか、しばら〜く悩みました。pagination と組み合わせようかとも思ったのですが、結局、改ページさせたいところで
if rowcounts >= 15
 xml << "</text></svg></page>"
 xml << "<page>"
 xml << '<svg x="0" y="0" viewBox="0 0 2970 2100" height="21cm" width="29.7cm">'
 xml.tag!("style", "type" => "text/css"){
  xml.cdata "\
  .col1 { x:200; dy:100;}\
  .col2 { x:400;}\
  .col3 { x:600; text-anchor:end;}\
 }
 xml << '<text font-size="40" y="500">'
 rowcounts = 1
end
みたいな感じで <page> を挟んで対処しました。 う〜ん。【xml <<】使うくらいなら、XmlMarkup 要らない気がするのは私だけ? でも、render :text => とかするよりは.rxml テンプレート使った方が判りやすいですよね、きっと……。
ああ、サンプルにスタイルシート入っていて見にくいですね。別に xml.cdata 使わなくても OK ですので。


上であげたサンプルコードは、実際に私が試したものとは異なります。このままで動くかどうかは保証の限りではありません。詳細については、それぞれのマニュアルなり、ヘルプページなりを参照して下さい。


使ってみれば、PXDoc は結構便利そうです。罫線も引けるし、網掛けも出来るし。グラフはまだ未挑戦です。後でやってみようかな。






サイト名: flyman のおもちゃ箱   http://www.kestrel.jp
この記事のURL:   http://www.kestrel.jp/modules/tinyd04/index.php?id=6