LibreOffice の力で PDF エクスポートする librepdf
現在大きな仕事を終えて、今は自身の知識の棚おろし中・・ノウハウの結晶化をしておきたいという個人的な動機から librepdf なんていう gem を公開しました。
https://github.com/hamajyotan/librepdf
ざっくりいうと、 LibreOffice の機能を用いて各種ファイルから PDF エクスポートできるというシロモノで、 MS Office 文書なんかも OK です。JRuby 専用、LibreOffice の UNO API を Java で叩いて、それを Ruby から使えるようにしています。まだまだまだ不安定ですが、きっと開示したほうがハッピーだなと思った次第です。
インストール
$ gem install librepdf
事前に LibreOffice をサービスとして起動する
$ /opt/libreoffice3.5/program/soffice.bin \ --accept="socket,host=127.0.0.1,port=8100,tcpNoDelay=1;urp;" \ --headless \ --invisible \ --nodefault \ --nofirststartwizard \ --nolockcheck \ --nologo \ --norestore
起動したら 8100 ポートのリスンを確認
$ netstat -ant | grep 8100 tcp 0 0 127.0.0.1:8100 0.0.0.0:* LISTEN
使い方サンプル
/home/hamajyotan/test.doc から、 /home/hamajyotan/test.pdf を作成
require 'rubygems' require 'librepdf' con = Connection.new "host" => "127.0.0.1", "port" => 8100 doc = con.load "file:///home/hamajyotan/test.doc" doc.convert_pdf "file:///home/hamajyotan/test.pdf" doc.close con.close
上記の後処理省力化的な書き方版
require 'rubygems' require 'librepdf' Librepdf::Connection.new "host" => "127.0.0.1", "port" => 8100 do |con| con.load "file:///home/hamajyotan/test.doc" do |doc| doc.convert_pdf "file:///home/hamajyotan/test.pdf" end end
僕はブレースの方が好み
require 'rubygems' require 'librepdf' Librepdf::Connection.new("host" => "127.0.0.1", "port" => 8100) { |con| con.load("file:///home/hamajyotan/test.doc") { |doc| doc.convert_pdf "file:///home/hamajyotan/test.pdf" } }
パスワード付きファイルを開く
require 'rubygems' require 'librepdf' Librepdf::Connection.new("host" => "127.0.0.1", "port" => 8100) { |con| con.load("file:///home/hamajyotan/test.doc", "Password" => "SECRETPASSWORD") { |doc| doc.convert_pdf "file:///home/hamajyotan/test.pdf" } }
ページを指定して PDF エクスポート
ページ範囲外の指定は例外が投げられます。
require 'rubygems' require 'librepdf' Librepdf::Connection.new("host" => "127.0.0.1", "port" => 8100) { |con| con.load("file:///home/hamajyotan/test.doc") { |doc| doc.convert_pdf "file:///home/hamajyotan/test1.pdf", "FilterData" => { "PageRange" => "1-1" } doc.convert_pdf "file:///home/hamajyotan/test2.pdf", "FilterData" => { "PageRange" => "2-2" } doc.convert_pdf "file:///home/hamajyotan/test3.pdf", "FilterData" => { "PageRange" => "3-3" } } }
クラスの説明
Librepdf::Connection
LibreOffice への接続を担う
- #initialize options = {}
- options ハッシュへは、 "host" と "port" が受理可能
- ブロックを渡すと、ブロック引数に自身のインスタンスを渡してブロック終了時にクローズする
- #closed?
- 接続を閉じていれば true, そうでなければ false
- #close
- 接続を閉じる
- #load input_url, options = {}
- ドキュメントをロードする
- ブロックを渡すと、ブロック引数にロードしたドキュメントのインスタンスを渡してブロック終了時にクローズする
- options ハッシュへは、 UNO の loadComponentFromURL の第4引数に渡すプロパティの一部が設定可能 ※
- ※ http://www.openoffice.org/api/docs/common/ref/com/sun/star/document/MediaDescriptor.html
Librepdf::Document
LibreOffice にロードされたドキュメントを示す、Librepdf::Connection#load の返り値として得る以外にインスタンス生成方法は無い。
正確には、このクラスのインスタンスではなく、ドキュメント種類に応じたサブクラスのインスタンスが生成される。例えば Librepdf::Document::Calc など。
- #closed?
- 接続を閉じていれば true, そうでなければ false
- #close
- 接続を閉じる
- #convert_pdf output_url, options = {}
- PDF エクスポートを行う
- options ハッシュへは、 UNO の storeToURL の第2引数に渡すプロパティの一部が設定可能
今後やりたいこと
- 例外が NativeException に丸められるのでどうにかする。
- LibreOffice を複数インスタンス起動している環境での接続プール
- ドキュメントのロード時に LibreOffice がブロックするのでマルチインスタンスはそれなりに有用