2010年11月アーカイブ

とあるプロジェクトで、通信のインターフェイスにXMLを使うことになっていて、顧客の指定で文字コードはSJISで!と言われたのでXMLの encoding は Windows-31J を使うことにした。
で、こちらのシステムはPHPを使っていたのであまり気にしていなかったのだけれど、対向のシステムが Java を使っていて、XML の文字列の要素をどうやってエンコードするんですか?と聞かれたので、普通に XML のシリアライザ使えば気にしなくて良いんじゃないの?と答えた。
そのときに、Java の変換関数があったら教えて、と言われたので、ちょっと自分で書いてみたらなかなかに謎だったので記す。

昔の Java は、xalan とか xerces とか外部ライブラリを使わないと XML が扱えなかったんだけど、最近の JDK は標準で使えるはず。
でも、SunがOracleに買収されたせいか、java.sun.com に繋がりにくくなって、APIリファレンスがみずらくてしょうがないので、適当にトライ&エラーで試した結果。
まずは、普通に XML をテキストにしてみる。試したのは、とあるLinuxで、LANGはja_JP.UTF-8。Javaは1.6.0_20。
        Transformer t = TransformerFactory.newInstance().newTransformer();
        t.transform(new DOMSource(document), new StreamResult(System.out));
これだと、普通に utf-8 の XML が生成される。あ、document は適当に中身を入れた org.w3c.dom.Documentね。
Windows-31Jにしたかったので、
        Transformer t = TransformerFactory.newInstance().newTransformer();
        t.setOutputProperty("encoding", "Windows-31J");
        t.transform(new DOMSource(document), new StreamResult(System.out));
とすると、出力されるXML宣言に encoding="Windows-31J"がつくようになり、本文もちゃんと Windows-31Jでエンコーディングされるんだけど、標準出力に
Warning:  The encoding 'Windows-31J' is not supported by the Java runtime.
と表示される。ちゃんと変換できてるのになんでそんな警告が出るんだろうか。
ちなみに、StreamResultのコンストラクタ引数は、OutputStream又はWriterを取れるので、
        Writer writer = new OutputStreamWriter(System.out, "Windows-31J");
        Transformer t = TransformerFactory.newInstance().newTransformer();
        t.transform(new DOMSource(document), new StreamResult(writer));
みたいに書くと、XML宣言のencodingはutf-8で、本文の文字コードは Windows-31JのXMLができあがる。
警告が気になるようであれば、
        Writer writer = new OutputStreamWriter(System.out, "Windows-31J");
        Transformer t = TransformerFactory.newInstance().newTransformer();
        t.setOutputProperty("omit-xml-declaration", "yes");
        t.transform(new DOMSource(document), new StreamResult(writer));
のように書いて、自分でXML宣言を前に付けてあげれば、望みどおりの出力が得られる。
あと、TransformerにXML宣言を出させた場合、
<?xml version="1.0" encoding="Windows-31J" standalone="no"?>
のようにstandalone="no"が付くのも制御する方法がわからなかった。

と、言うわけで、なんで Windows-31J を runtime がサポートしてないよって言われるかが謎って言う話。誰か教えてください。
iOSが4.2.1になって、AirPrintに対応したらしい。
当面は AirPrint に対応した HP のプリンタだけが対応らしいけど、MacとかWindowsだと非公式なパッチとかドライバっぽいので AirPrint のプリンタ共有ができるらしい。
うちの印刷環境は、FreeBSD にパラレルで Canon の Pixus 560iと言うプリンタを繋いで、samba 経由で Windows から印刷できるようにしている。
印刷のバックエンドは、いわゆる bsd の lpr で、一応 FreeBSD のコマンドラインからと、Windows から samba での印刷ができるようにしていた。
で、samba でも AirPrint のプリンタ共有ができないかな~と思って調べていたら、micromux » AirPrint for Mac on Linuxと言うページを発見。
CUPS と avahi ってのを使うと AirPrint に対応できるらしい。
と、言うわけで今までずっと避けていた CUPS に挑戦するのである。

  1. cupsのインストール
    portsに、print/cupsと言うmetaportがある。うちの場合、sambaの依存関係で、cups-clientと言うportsのみインストールされていたので、まずはこいつを試す。
    が、いきなりソースを展開した後 cd できなくてエラーになったので、metaportが依存している print/cups-base をビルドすることに。
    すると、tls関連でリンクエラーになって進まない。エラーを良く見ると cups-base ではなく cups-client でこけているので、まずは portupgrade cups-client してclientを最新にする。
    すると、無事に cups-base もインストールすることができた。
    ビルドのオプションは、デフォルト+PYTHON(後述)
    最初にprint/cupsでビルドしたときに、print/cups-pstoraster がインストールされていたので、これもあった方が良いかも。
  2. cupsの設定
    cups-baseのpkg-messageを読むと、以下のように書いてある。
    ======================================================================
    
    To enable printing with local printer you need to give group 'cups'
    r/w access to printer device:
    
    1) Add following to /etc/devfs.rules (create if it doesn't exist):
    
    [system=10]
    # FreeBSD 7.x
    add path 'unlpt*' mode 0660 group cups
    add path 'ulpt*' mode 0660 group cups
    add path 'lpt*' mode 0660 group cups
    # FreeBSD 8.x
    add path 'usb*' mode 0770 group cups
    add path 'ugen*' mode 0660 group cups
    
    2) And following to /etc/rc.conf:
    
    devfs_system_ruleset="system"
    
    3) Restart devfs: /etc/rc.d/devfs restart
    
    If your system supports 'devd' you can copy
    $PREFIX/share/examples/cups/lpt-cupsd.conf to $PREFIX/etc/devd/
    
    To enable printing under Gimp and MS-Windows clients do the following:
    
    1) Uncomment application/octet-stream line in mime.types
    2) Uncomment application/octet-stream line in mime.convs
    3) Restart cupsd
    
    If you are using libusb, it is important that no device driver, e.g.
    ulpt(4) is attached to the device you wish to use. In this case please
    ensure the cups user and group has read/write access to /dev/ugen*
    
    If you are using a USB printer wtih FreeBSD 8.0 or later, you will
    need to find the proper /dev/usb/* device pointed at by the /dev/ugen*
    entry. Follow the instructions for devfs.rules as above, but append a
    rule similar to the following for a printer attached as /dev/ugen0.2:
    
    add path 'usb/0.2.*' mode 0660 group cups
    ======================================================================
    
    これにしたがって、/etc/devfs.rulesを作成し、/etc/rc.conf に devfs_system_ruleset="system" を書いて /etc/rc.d/devfs restart した。
    devdもあるっぽいので、lpt-cupsd.conf とか、Windowsから印刷するのでmime.typesの変更とかいるのかも知れない。→mime.typesの該当の記述は、最初からuncommentされていた。
  3. /etc/rc.conf に cupsd_enable="YES" を追記して、/usr/local/etc/rc.d/cupsd start でcupsdを起動。
    ブラウザから、http://サーバ:631/ にアクセスすると、cupsの管理画面が出る。
    プリンタの追加を選んで、ローカルプリンタを選ぶと、機種の選択画面になる。(前述のdevfs関連をやっていないと、ローカルプリンタが出てこない)
    Pixus 560i(と言うかCanon)は選択肢にないので、ppdファイルと言うのが必要になる。
    ちょっと調べてみると、Canon Bubble Jet Print Filter Ver.2.40 for Linuxのページの一番下にソースファイルと言うところがあるので、そこにある「CUPS追加モジュール bjcups, ppdファイル, フィルタソース一式。」と言うのを持ってくる。
    中にある canonpixus560i.ppd と言うファイルをcupsの設定画面からアップロードしてやると、なんとなく設定が完了する。
    で、プリンタの一覧なんて見ると、
    待機 - "Filter "/usr/local/libexec/cups/filter/pstocanonbj" for printer "lp" not available: No such file or directory"
    
    とか言うエラーが出ている。
    pstocanonbjなんてどこにあるんだ~と思ったら、今ダウンロードした bjcups-2.4-0.tar.gz にソースがあった。自分でビルドする必要があるらしい。
  4. bjcupsのインストール
    このファイルはLinux用だが、FreeBSDでも使えるだろうと思ってビルドすることにする。
    ばっちりコンパイルエラーが出るので、FreeBSDでコンパイルできるようにパッチを作成した。
    ソースを展開したディレクトリで、patch -p1 < bjcups-2.4-0.patch とかやるとパッチがあたるので、INSTALLに書いてある通りにlibsでmake && make installして、ソースディレクトリでmake && make installすればインストールされる。

    →結局、上記を入れても、今度は /usr/local/bin/bjfilterpixus560i: not found と言うエラーになってしまう。canonのページから bjfilter-2.4-0 を持ってきてみたが、こちらはLinux用のバイナリ配布のライブラリを要求するので断念した。
  5. cups-imageのインストール
    これで行けるかと思ってcupsにプリンタを登録しなおすと、
    待機 - "Filter "/usr/local/libexec/cups/filter/commandtops" for printer "lp" not available: No such file or directory"
    
    と言うエラーが出る。
    ちょっと調べた感じだと、commandtopsはcups-baseでは入らないらしいので、portsからprint/cups-imageをインストールする。
  6. gutenprint-cupsのインストール
    さらに調べると、ports/print/gutenprint-cupsの中にgimpのcanonのドライバがあるようなので、こちらをインストールする。
    試行錯誤しながらやっているので、ひょっとしたらこちらを使う場合でもcups-imageは必要かも知れない。
    gutenprint-cupsを入れると、CUPSのプリンタの追加にCanonが出てくるので、i560を選択してみた。(Pixus 560iそのものはないので、他のサイトにあるようにBJC-8200を選ぶほうが良いのかも知れない)
    ここまでで、CUPSのプリンタの管理ページからテストページを印刷すると、日本語らしき部分が文字化けしてるけど一応印刷される。CUPSのテストページが化けるのは bannertops が悪いみたいな記述をしているページも見かけたので、とりあえずスルー。
  7. avahiのインストール
    portsから、net/avahi-app をインストールする。
  8. airprint-generateの実行
    # git clone https://github.com/tjfontaine/airprint-generate.git
    
    でairprint-generate.gitを持ってくる。airprint-generateディレクトリができるので、そこにあるairprint-generate.pyを実行する。
    すると、cupsなんてモジュールは知らないと言われる。
    ここで、cups-baseをconfigしなおしてインストールしたりして嵌ったんだけど、結局はportsからprint/py-cups をインストールすることで解決した。
    これで、airprint-generate.pyを実行すると、AirPrint-lp.service なんてファイルができる。(lpはcupsでプリンタにつけた名前)
    AirPrint-lp.service を /usr/local/etc/avahi/services/ にコピーする。
  9. avahi-daemonの起動
    /etc/rc.conf に avahi_daemon_enable="YES" を追記して、/usr/local/etc/rc.d/avahi-daemon start 。すると、messagesに
    WARNING: No NSS support for mDNS detected, consider installing nss-mdns!
    dbus_bus_get_private(): Failed to connect to socket /var/run/dbus/system_bus_socket: No such file or directory
    WARNING: Failed to contact D-Bus daemon.
    
    とかでて起動しない。
    portsのdevel/dbusとか言うのがインストールされていたが古そうだったので、portupgrade dbusして、/etc/rc.conf に dbus_enable="YES" を書く。
    dbus と avahi-daemon を起動すると、
    WARNING: No NSS support for mDNS detected, consider installing nss-mdns!
    
    だけになって、avahi-daemonが起動したっぽい。
  10. iPadから印刷
    iPadのSafariからプリントを選ぶと、プリンタを検索する。
    最初は検索しても見つからなくてあせったのだが、しばらくほっておいたら表示されるようになった。タイムラグがあるのかも知れない。
    で、プリンタを選択して印刷すると、プリンタはうんともすんとも言わない。
    cupsのerror_logを見ると、
    Request from "192.168.0.82" using invalid Host: field "sv.local"
    
    とか言うエラーが出ている。
    ちなみに、192.168.0.82がiPadのアドレスで、cupsサーバのホスト名はsv.wizard-limit.net。
    messagesを見ると、
    Static host name sv.wizard-limit.net: avahi_server_add_address failure: Not supported
    
    とか出ている。
    avahiの設定ファイルのhostsを見ると、ホスト名を書くようだったので追記してみたりしたが状況は変わらず。
    avahi-daemon.conf の host-name と domain-name がコメントアウトされているので、ここに記述すれば良いのかもしれない。→host-nameとdomain-nameを記述すると、
    Failed to add service 'AirPrint lp @ sv' of type '_ipp._tcp', ignoring service group (/usr/local/etc/avahi/services/AirPrint-lp.service): Not supported
    
    とか怒られてうまくいかなかった。
    とりあえず、cupsd.conf に
    ServerAlias sv.local
    
    と書くことでエラーは回避できた。
    ちなみに、最初に調べた micromux » AirPrint for Mac on Linux の手順では、ServerAlias * と書いてあったが、CUPSのマニュアルには * は推奨しないと書いてあったので使わないほうが良いかもしれない。
    再度印刷してみると、cupsのaccess_logに
    "POST /printers/lp HTTP/1.1" 200 199 Validate-Job client-error-not-found
    "POST /printers/lp HTTP/1.1" 200 70880 Print-Job client-error-not-found
    
    って言うのが繰り返し出る。そして、しばらくするとiPadがsleepしてしまう。当然印刷はされない。
    CUPSの管理画面でデバッグログを有効にしてみると、error_logに
    Validate-Job ipp://sv.local.:631//localhost:631/printers/lp
    
    とか出てる。ipp://sv.local.:631/printers/lp か http://localhost:631/printers/lp のどちらかが正しいのではないかと思うのだが。
    テストページが印刷できているのだから、おそらくavahiの方が悪いのだろうと思って、AirPrint-lp.service を眺めてみると、rp=//localhost:631/printers/lp とか書いてある部分がある。typeにippとか書いてあるし、portに631とか書いてあるので、これはきっと余計だろうと思って rp=/printers/lp に変更してみる。
    avahi-daemonを再起動して、再度iPadから印刷してみると、
    "POST /printers/lp HTTP/1.1" 200 184 Validate-Job successful-ok
    "POST /printers/lp HTTP/1.1" 200 71361 Print-Job successful-ok
    
    と出て、無事に印刷された。日本語もちゃんと出てる。
とりあえずlpdも常駐したままで喧嘩していないみたいなので、FreeBSDとWindowsからは今まで通りlpd経由で、iPad/iPhoneからはCUPS経由で印刷ができそうだ。
airprint-generate.py がおかしいとは思えないので、たぶんCUPSの設定かavahi-daemonの設定がどこか間違っているのだろうと思うけど、とりあえず印刷できたからよしとする。
iPadから印刷できたので、当初の要求は満たしたのだけれど、一応FreeBSDからも印刷したくていろいろ試してみたが、日本語がことごとく駄目。
日本語を試すのに、テキストファイルと、TeXのdviファイルを利用したくて 、ちょっと見てみたら今の環境にはTeXが入っていなかった。
昔は、ASCII版のplatexがports/japaneseにあったのだけれど、最近はどうも違うらしい。
そこで、ports/japanese/teTeXと言うmetaportがあったのでそれを入れてみた。
teTeXをインストールするとplatexコマンドが使えるようになったので、むかーし書いたtexファイルをかけてみると、「Entering LaTeX 2.09 COMPATIBILITY MODE」とか出て警告がぞろぞろ出た。
最近のLaTeXがどうなってるのか全然わからないので、勉強しないと駄目だなあ。
とりあえず、dvipsも入っているので dvipsをかけると psファイルができる。
こいつをcupsの/usr/local/bin/lpr で印刷してみると、日本語部分が文字化け。
dviファイルがおかしいのか、psファイルがおかしいのか、印刷部分がおかしいのか判断がつかなかったので、順番に切り分けることに。
まずは、xdviでdviファイルを見てみると、文字化けせずに表示されたのでdviファイルは問題なさそう。
続いて、ps ファイルをgs で見ようとすると、gsが落ちてしまう。
そこで、ghostscriptとか、gsfontsとか関係しそうなportsを最新にして、japanese/font-ipa をインストールしたら、無事にgsでpsファイルが見られるようになった。
これで、psファイルも問題なし。
どこで入れたのかわからないけど、dvipdfと言うコマンドもあったので、試してみたところ、生成されたpdfを/usr/local/bin/lpr で印刷すると文字化けせずに印刷された。
???
とりあえず、bannertopsとtexttopsは日本語が駄目らしいと言う情報があったので、pstopsもまずいのではないかと疑ったりしたが、pstopsを通したpsファイルもgsで表示できたので違うようだ。
次に疑ったのが pstorasterで、こいつは ports/print/cups-pstoraster でインストールされるもの。
cups-pstorasterのMakefileを読むと、WITH_CJKと言うオプションがあるらしいのでそれを有効にしてみた。
すると、今まで文字化けして印刷されていたのがエラーが出て印刷できなくなった。
Set job-printer-state-message to "/rangecheck in --string--", current level=ERROR
Operand stack:
Fa   256   --nostringval--   --nostringval--   0   79.8851   Ryumin-Light-H   Font   Ryumin-Light-H   H   --nostringval--   FDepVector   --nostringval--   0   Ryumin-Light   CIDFont   true   Ryumin-Light   Ryumin-Light   --nostringval--   --dict:15/18(L)--   --nostringval--   236166   236166
Execution stack:
%interp_exit   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--   --nostringval--   --nostringval--   false   1   %stopped_push   1   3   %oparray_pop   1   3   %oparray_pop   1   3   %oparray_pop   1   3   %oparray_pop   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--   --nostringval--   7   4   %oparray_pop   8   4   %oparray_pop   --nostringval--   --nostringval--   --nostringval--   11   5   %oparray_pop   --nostringval--   1   1   0   --nostringval--   %for_pos_int_continue   --nostringval--   16   6   %oparray_pop   --nostringval--   --nostringval--   --nostringval--   --nostringval--   --nostringval--   --nostringval--   --nostringval--   --nostringval--   --nostringval--   --nostringval--   %array_continue   --nostringval--   --nostringval--   --nostringval--
Dictionary stack:
--dict:1089/1123(ro)(G)--   --dict:0/20(G)--   --dict:103/200(L)--   --dict:85/300(L)--   --dict:47/65(ro)(G)--   --dict:3/10(L)--   --dict:38/50(ro)(G)--   --dict:28/50(ro)(G)--   --dict:15/40(L)--
Current allocation mode is local
ESP Ghostscript 815.04: Unrecoverable error, exit code 1
何が悪いかさっぱりわからなかったけど、どうせgsもインストールされているのだからとそちらを試してみることにした。
/usr/local/libexec/cups/filter/pstoraster をエディタで開き、espgs を呼び出しているところを gs に変更する。
--- pstoraster.ORIG     2010-11-27 16:18:35.000000000 +0900
+++ pstoraster  2010-11-27 20:45:50.000000000 +0900
@@ -52,10 +52,10 @@
 fi

 echo INFO: Starting GNU/GPL Ghostscript...  1>&2
-echo DEBUG: Running $bindir/espgs $gsopts -sOUTPUTFILE="%stdout" -c"$profile" "$ifile" 1>&2
+echo DEBUG: Running $bindir/gs $gsopts -sOUTPUTFILE="%stdout" -c"$profile" "$ifile" 1>&2

 # Now run Ghostscript...
-exec $bindir/espgs $gsopts -sOUTPUTFILE="%stdout" -c"$profile" "$ifile"
+exec $bindir/gs $gsopts -sOUTPUTFILE="%stdout" -c"$profile" "$ifile"

 #
 # End of "$Id: pstoraster.in,v 1.3.2.4 2004/06/29 13:15:10 mike Exp $".
すると、無事に印刷され、日本語も化けずに印字できた。
ならばと思ってテストページを印刷してみたが、こちらは化けたまま。やはりbannertopsが悪いのだろうか。

ちなみに、テキストファイルについては texttops が使えないので、t2psをインストールして texttops を置き換えるような対応ができるようだ。
2010年11月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        

このアーカイブについて

このページには、2010年11月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2010年10月です。

次のアーカイブは2010年12月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Powered by Movable Type 6.1.1