Graphics Context
ドキュメントを流し読みして、なんとなく感じをつかんでみる。
イメージを描画するのは、なんとかContextと名前のついている「グラフィックスコンテキスト」だそう。最近は何かにつけコンテキストという言葉が出てきますねー。
グラフィックスコンテキストは
- Bitmap Graphics Context
- PDF Graphics Context
- Window Graphics Context
- OpenGL Graphics Context
- Postscript Graphics Context
があるそうです。
今やりたい事はファイルを変換する事だから、画面に描画するようなWindow Graphics Contextなどは関係なさそう。さしあたり、Bitmap Graphics ContextとPDF Graphics Contextが関係しそうです。
処理の流れとしては、
- BitmapContextを用意する。
- PDFを読み込む。
- そのコンテキストにPDFの中身を描画する。
ってな具合で行けるのではないかと思われます。
ただ、PDFはページと言う概念があるので、
- 変換元のPDFが何ページあるか調べる。
- ページ数分だけ、処理を繰り返す。(そのページサイズでBitmapContextを作って描画する。)
という事になると思います。
だいたい目星がついたところで、(すごい適当だ!(笑))、サンプルのソースを見てみる事にします。
ドキュメント
で、まずはドキュメント。
Quartz 2D Programming Guide
http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/index.html
ざっと概要を読んでみると、
- Quartz 2D ってのは、Mac OS Xがもつ描画エンジン。
- Core Graphics frameworkの一部だそうで、PostScriptとPDFに基づいている。
- カラーマネージメント(ColorSync)をサポート。フォトレンダリングにApple Type Serviceを使ってるのだそうです。解るような解らないような(笑)
- 要するにローレベルなAPIで、軽い。
なんて事が書いてあります。
自分のやりたい事は、サーバー上でPDFをJPEGに変換したいという、別の意味でローレベルな要求(笑)なのですが、CMYK画像の色がちゃんと出せたり、フォントがきれいにレンダリングできるのならば、それは願ったり叶ったりです。
なお、開発はPythonのラッパーがありますので、それをありがたく使わせていただきます。
サンプルのスクリプトはDeveloper's Toolsをインストールすると/Developer/Examples/Quartz/Pythonに入っていますので、これらを参考にします。うーん、お気軽だ。
Python Bindings for Quartz 2D 〜導入〜
たまにはMacネタでも。
Mac OS X はTigerになって、PDFのサポートがかなり良くなりました。
サポートするPDFのバージョンは1.4という事で、これなら印刷屋的にも良い感じであります。
よくサーバー上でPDFをJPEGなんかに変換したい!なんて事はよくある(?)話だと思います。Mac OS Xを使っているならば、こいつのAPIをそっと叩いてJPEG変換してみるという手もあります・・というお話です。
そもそも、PDFをJPEGなどのビットマップに展開するユーティリティーなんて沢山ありますけど、日本語を扱う場合にいつもフォントの問題がつきまといます。GhostScriptなんかだとかなりいい感じで変換できますが、思うようにいかない事もしばしば・・。
印刷屋的に、「フォント」問題は「死活」問題なので、結構苦労します。PDFなんだから埋め込みゃいいじゃん!という話もありますが、それはそれでファイルサイズが大きくなったり、そもそも埋め込めないフォントがあったりと苦労も耐えません(涙)
そんなこんなで、ここ2年くらいの悩みの種だったんですが、OS XもTigerになり、PDFのサポートがかなり使えるようになってきました。しかも、このAPIがPythonからアクセスできると言うおまけ付き!!という事もあり、サーバで使える変換プログラムを作ってみようかなぁ・・という実験です。
プライマリキーの扱い
Cayenneのプライマリキーの管理は、基本的に「おまかせ」です。ほかのO/Rマッパーはよく知りませんが、EOFも同様です。
たとえば新しくデータをインサートする時などはプライマリキーの生成もCayenneが行ってくれます。そのための特別なテーブル(AUTO_PK_SUPPORT)が作られる場合もあります。(EOFではAUTO_PK_TABLEでしたっけ?)
CREATE TABLE AUTO_PK_SUPPORT ( TABLE_NAME CHAR(100) NOT NULL, NEXT_ID INTEGER NOT NULL);
例えばEmployeeというテーブルを用意すると
CREATE TABLE Employee (empno INTEGER NOT NULL, ename VARCHAR(16) , sal FLOAT, PRIMARY KEY (empno));
コレに対応するEmployeeクラスは
Employee.java
public class Employee extends DataObject { public void setEname(String ename) {...} public String getEname() {...} public void setSal(Float sal) {...} public Float getSal() {...} }
のような感じになります(ソースは適当です)。つまり、プライマリキーはユーザー側ではいっさい見ません。(empnoのゲッター/セッターがありません)
新しくEmployeeを追加する場合は、AUTO_PK_SUPPORTテーブルに以下のようなSQLが自動的に発行されて、新しいプライマリキーを発行します。
SELECT NEXT_ID FROM AUTO_PK_TABLE WHERE TABLE_NAME = 'Employee';
NEXT_IDをインクリメントして、
UPDATE AUTO_PK_TABLE SET NEXT_ID = NEXT_ID + 20 WHERE TABLE_NAME = 'Employee';
ただし、使用するデータベースによっては、AUTO_PK_TABLEを使用せずに、データベース自身が持つシーケンスなんかをつかう場合もあるようです。詳しくはドキュメントの4.7.3 Generated Primary Keyを参照してください。
もちろん、
public void setEmpno(Integer empno); public Integer getEmpno();
をEmployeeオブジェクトに定義しても構いませんし、問題なく動作します。この場合はAUTO_PK_SUPPORTにUPDATEをかけるような動作はしません。プライマリキー管理はユーザ側で責任持ってねという事になります。
ちなみにプライマリキーを参照するメソッドが定義されていない場合でも、DataObjectUtilクラスでプライマリキーの値を取ってくる事は可能です。(EOFのEOUtilities.primaryKeyForObject()に相当。)
おそらくCayenneの住人(って、日本にいるんだろうか・・・?)、EOF界隈の人たちはプライマリキーについてあまり意識した事無いんじゃないかなあ?(一部から反発食らいそうですが(笑))
もちろん、プライマリキー自体に「意味がある」ってのはかなり多いと思いますんで、その辺の事はドキュメント 4.7.1 Meaningful Primary Key にも言及されています。
で、結局の所、キーの管理はCayenneにお任せでも、自分で管理しても、どちらでも構わないんですが、S2Cayenneでちょっと困った事がありまして・・
このプライマリキーの自動生成ですが、AUTO_PK_SUPPORTにアクセスする際、否応無しにInternalTransactionを使うんですよー。こりゃ参ったなー。Cayenneのバグなのかも??ちょっと調査してますよー。
Cayenneのトランザクション
現在、Cayenneのトランザクションについて、いろいろと調べています。
CayenneはもともとJTA準拠なんてぜんぜん考えていなくて、トランザクション制御はCayenne自身が行っていました・・っていうか、今でもそうです。ただし、現状ではトランザクション制御はコンテナなど「外部にお任せする」というオプションがつきました。
DataDomain.setUsingExternalTransactions (boolean flag)
もしくはCayenneModelerでContainer-Managed Transactionsにチェックを入れます。
直接モデルファイルに
<property name="cayenne.DataDomain.usingExternalTransactions" value="true"/>
を挿入してもOKで、いずれもやっている事は同じ事です。
Cayenneにはorg.objectstyle.cayenne.access.Transactionというそのものずばりのクラスがあります。これは抽象クラスで、実際はトランザクション制御を外部に委託する場合にはTransactionのサブクラスであるExternalTransactionが使用され、Cayenneにおまかせする場合はExternalTransactionクラスのサブクラス(Transactionのサブクラスじゃないよ。ややこしいけど)であるInternalTransactionクラスが使用されるようです。
ちなみにExternalTransaction、InternalTransactionはTransactionのインナークラスになってまして、JavaDocには出てきません(涙)。
また、名前はTransactionですが、JTAとは全く関係ありません。
DataContextがデータベースにアクセスするに、DataDomain.createTransaction()メソッドが呼ばれるようになっています。そこでDataDomainにセットされたUsingExternalTransactions属性によりExternalTransactionかInternalTransactionのインスタンスが返ってくるという仕掛けのようです。
で、S2Cayenneの場合、トランザクション制御はSeasarにやらせたい訳で、ExternalTransactionを使用する事になるんですが、こいつは何をするクラスかと言うと・・単純にcommit()やrollback()時にconnectionをcloseするというものです。(内部的にステータスの管理やTransactionDelegeteの通知なども行っているようです。)
トランザクション制御は外部が行うので、自分は基本的には何もしない・・という訳です。考えてみたらそりゃそうなんですけど、JTAのTransactionとCayenneのTransactionが両方登場して、軽くパニックになります・・
と言う訳で、やるべき事はSeasarが与えてくれるDataSourceをCayenneに与えば良い事になります。たぶん。
SeasarのDataSourceをCayenneに与える方法としてもっともスマートそうなのが、org.objectstyle.cayenne.conf.DataSourceFactoryを実装してあげて、これをConfiguration.setDataSourceFactory()してあげます。
うーむ。
うぅ・・WRさん経由(http://www.csus4.net/d/2005/07/18/s2cayenne/)でS2Cayenneの話が舞い込んできましたー。
id:koichikさんから直にトラックバックをいただき、ちょっとビビる(笑)。
どうしたものかなぁ・・まずは、ちゃんとSeasarを理解しなきゃだし、id:aua4ioさんの悩み(http://d.hatena.ne.jp/aua4io/20050701/1120205958)も今はあまり理解できてませんし・・(すんません)
でも、Seasarのプロジェクトに微力でも関われるなら非常に喜ばしい事なので、ぼちぼち頑張ってみようかなー?と思います。