レベル: 中級 Jake Miles, Senior Developer, Conde Nast
2008年 12月 09日 XML 文書から PostScript ファイルを生成するプログラミング言語コンパイラーとしての XSLT の概念を、特に PostScript® の前面に XML ファサードを作成するという観点から詳しく見ていきましょう。この記事では、暗黙的な言語定義としてのスタイルシートの概念、PostScript の基礎知識、そして XML から PostScript へのコンパイラーを作成する際に必要な抽象化層について説明します。
コンパイラーとしての XSLT
 |
よく使われる頭字語
- CSS: Cascading Stylesheet
- JVM: Java Virtual Machine
- PDF: Portable Document Format
- RTF: Rich Text Format
- XHTML: Extensible Hypertext Markup Language
- XML: Extensible Markup Language
- XSLT: Extensible Stylesheet Language Transformation
|
|
XSLT は、XML データを他のデータ・フォーマットに変換するための手段です。一般に XSLT は、XML 文書を入力として取り、その文書を別の XML 構造 (XHTML を含む) に変換する XML 間の変換言語だと思われていますが、実は XML だけでなく、他の出力を生成する場合にも使用することができます。このように考えると、XSLT スタイルシートは実際には新しい言語を定義するためのツールであり、言語コンパイラーとみなすことができます。コンパイラーという言葉を使うのは、奇異に思われるかもしれません。コンパイラーは人間が理解できるコードをマシン言語または仮想マシンのバイトコードに変換するプログラムだと考えられていますが、一般的な意味でのコンパイラーは単なる変換機構で、ある言語を別の言語にコンパイルするためのものです。大抵の場合、ターゲット言語はマシン・コードなどのバイナリー・フォーマットですが、いつもそうであるとは限りません。この点から言うと、XSLT は 1 つのコンパイラー技術です。XSLT スタイルシートは新しい言語として XML 方言を暗黙的に定義し、これを別の形式にコンパイルすることによって、別の XML 方言や XML 以外の言語にすることができます。
コンパイラーとしての XSLT の概念を説明する一例として、XML から Java™ コードを生成する場合を考えてみてください。生成する Java コードが例えばオブジェクト・リレーショナル・マッピング・システムの一部となる Java Bean だとすると、XSLT を使用してこれを生成することができます。Java Bean はデフォルト・コンストラクターを持つ Java クラス (引数を取らないクラス) で、getter メソッドと setter メソッドを公開します。呼び出し側はこれらのメソッドを使用して、オブジェクトのプロパティーを読み取り、設定します。リスト 1 は、XML ベースの言語で Java Bean クラスを定義する XML 文書の例です。
リスト 1. Java Bean クラスを定義する XML 文書
<java>
<bean-class name="Employee">
<property name="id" type="int"/>
<property name="name" type="String"/>
<property name="title" type="String"/>
</bean-class>
</java>
|
上記の XML 文書は、プロパティーの数を指定し、それぞれのプロパティーに名前と Java の型を設定して 1 つの <bean-class> 要素を作成しています。Java Bean クラスのプロパティーを指定する上でも、XML はかなり簡潔な言語です。リスト 2 に、この XML から Java コードを生成する XSLT を記載します。
リスト 2. XML から Java Bean コードを生成する XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="bean-class">
public class <xsl:value-of select="@name"/> {
<xsl:apply-templates select="property" mode="instance-variable"/>
public <xsl:value-of select="name"/> () { }
<xsl:apply-templates select="property" mode="getter-and-setter"/>
}
</xsl:template>
<xsl:template match="property" mode="instance-variable">
private <xsl:value-of select="@type"/> <xsl:text> </xsl:text>
<xsl:value-of select="@name"/>;
</xsl:template>
<xsl:template match="property" mode="getter-and-setter">
<xsl:apply-templates select="." mode="getter"/>
<xsl:apply-templates select="." mode="setter"/>
</xsl:template>
<xsl:template match="property" mode="getter">
public <xsl:value-of select="@type"/>
get_<xsl:value-of select="@name"/>() {
return this.<xsl:value-of select="@name"/>;
}
</xsl:template>
<xsl:template match="property" mode="setter">
public void set_<xsl:value-of select="@name"/>(<xsl:value-of
select="@type"/> value) {
this.<xsl:value-of select="@name"/> = value;
}
</xsl:template>
</xsl:stylesheet>
|
最初のテンプレートは <bean-class> 要素との突合せを行い、XSLT 文書が通常行うように XML 要素を出力するのではなく、Bean クラスの Java コードをリテラル・テキストとして出力します。このテンプレートはまず、クラスの専用インスタンス変数を生成するために <property> サブ要素を instance-variable テンプレート (instance-variable モードで “property” 要素を突き合わせるテンプレート) に渡します。次に、デフォルト・コンストラクターを生成し、<property> 要素を getter-and-setter テンプレートに渡してプロパティーの getter メソッドと setter メソッドを生成します。このスタイルシートを介してサンプル XML を渡すと、リスト 3 の結果になります。
リスト 3. Java Bean クラスを生成する XSLT スタイルシートの出力
public class Employee {
private int id;
private String name;
private String title;
public Employee() { }
public int get_id() {
return this.id;
}
public void set_id (int value) {
this.id = value;
}
public String get_name() {
return this.name;
}
public void set_name (String value) {
this.name = value;
}
public String get_title() {
return this.title;
}
public void set_title (String value) {
this.value = value;
}
}
|
XSLT を使用して新しい言語を定義する方法は以上のとおりです。このように、XSLT スタイルシートは Java クラス定義を指定するために新しい言語を暗黙的に定義します。このスタイルシートをオブジェクト・リレーショナル・マッピング・システムでサンプルとして使用すれば、テーブルの列から XML が生成されるデータベース・テーブルに対応した Java オブジェクト、あるいは開発者が直接 XML を提供するユーザー指定の XML 構成に対応した Java オブジェクトを生成することができます。1 対 1、1 対多、および多対多のカーディナリティーといったクラスの関係を簡潔に定義するためのサポートを追加するには、XML 方言とそれに対応する XSLT スタイルシートを具体化します。この場合のスタイルシートが生成するのは、子オブジェクトのリストを管理するためのかなり複雑なコード、そして SQL データベースを対象としたオブジェクト・ツリーの読み取り操作と保管操作を行うためのコードです。ソース言語 (Java オブジェクトを指定するために使用する XML 方言) をいったん定義すれば、XML をどんなに複雑なコードにでも変換できるようになると同時に、その実装ロジックのすべてを、上位レベルでの XML 言語の観点からオブジェクト・システムを定義すればよいだけのエンド開発者に対して隠すことができます。
XSLT が定義する新しい言語
以上のコンテキストで考えると、あらゆる XSLT スタイルシートは新しい言語を暗黙的に定義していることになります。XSLT スタイルシートが XML を別の形式に変換するのは、Java コンパイラーが Java ソース・コードを一連の仮想マシン命令に変換するのと同じだからです。唯一の制約は、XSLT は XML 入力を処理するように設計されているため、ソース言語は XML 方言として定義しなければならないことだけです。
別の観点から見てみると、Java 言語は JVM の命令セットに対する単なるラッパーとして捉えることができるのと同じように、XSLT も他の言語と出力フォーマットに対する XML ラッパーを定義するために使用することができるのです。
XML は極めて普及している標準で、利用できるサポートも豊富です。そのため、言語を XML 方言として定義すれば、その言語は容易に理解され、他のシステム (すでに XML 出力を生成するように設計されているシステムなど) に簡単に統合することができます。また、XML を利用すればそうした言語を XSLT パイプラインに組み込むことも可能になり、ソース XML データを、目的の出力フォーマットをラップする新しい言語に変換することもできるようになります。PDF や PostScript などの文書に関する言語構文の詳細知識がなくても、これらの文書をプログラムで生成できるようにし、その出力フォーマットをラップする手段としては、至るところに普及している XML が第一の候補となります。
XML から生成する Adobe PDF
Adobe はすでに、Adobe Mars プロジェクトで XML との論理的関連付けを実現しています。このプロジェクトで Adobe が定義した PDFXML は、PDF 文書を作成するために使用できる XML 方言です (「参考文献」を参照)。Mars プロジェクトは目下開発が進んでいるところで、ツールおよびチュートリアル・ドキュメントという点では最小限のサポートしか用意されていません。また、Mars 文書は既存の PDF ビューアーでは表示できないため、表示用の特殊なプラグインが必要です。つまり、これは移植可能な文書を作成するためのまったく新しいプラットフォームであるということです。そのため、既存の PDF ビューアーの世界との互換性はありません。
XML ベースの文書フォーマットに対する別の方法は、XML から既存のフォーマットに変換するための XSLT スタイルシートや一連のスタイルシートを使って PDF などの既存のフォーマットに変換可能な XML 方言を定義する方法です。
XML 構造を直接 PDF コードに変換するための XSLT スタイルシートを定義する方法もあります。この場合、おそらく Java iText プロジェクトのコードをスタイルシート・ロジックの参考にします。
XML から PDF を生成するもっと単純な例は中間ステップを踏む方法で、XML を PostScript に変換した上で、PostScript から PDF へのオープンソースの変換プログラムを使用して PDF フォーマットへの最終コンパイル・ステップを行うという手法です (「参考文献」に、PostScript から PDF へ変換するオープンソースのツールへのリンクをいくつか記載しています)。その最終的な結果として、XML を使用して PDF 文書をプログラムで作成できるようになります。
単純な例: XML から PostScript を生成する
この単純な例では、まずは PostScript を生成し、それからオープンソースのコマンドライン・ツールを使って その PostScript を PDF フォーマットに変換することができます。最初に考えなければならないのは、XSLT をどの程度使用するかです。PostScript はオペランドをスタックにプッシュしてから関数を適用するプログラミング言語です。この後置 (postfix) 構文と呼ばれる表記法では、関数名は引数の後に指定します。つまり、3 と 7 を乗算する場合の PostScript コマンドは、3 7 mul となります。これは、「3 をスタックにプッシュし、次に 7 をスタックにプッシュし、それから mul 関数を呼び出す」ことを意味します。mul 関数が呼び出されると、最初の 2 つの項目をスタックからポップして乗算を行い、その結果をスタックに配置します。したがって、3 7 mul 2 add という PostScript コマンドは演算式 (3 * 7) + 2 に相当し、mul 関数が 3 と 7 を乗算してスタックに 21 を残し、続いてリテラル 2 をスタックにプッシュして add 関数を呼び出します。この関数がスタックから最初の 2 つの項目をポップして加算して、スタックに 23 を残すというわけです。
PostScript は、テキストとグラフィックからなる印刷可能なページをレンダリングします。テキストとグラフィックのオブジェクトをページ上に配置するには、上記の乗算と加算の例と同じ後置構文を使用します。PostScript の座標系では、原点 (0,0) はページの左下隅に置かれ、1 インチにつき 72 ポイントが含まれます。つまり、座標 (72, 72) が表すのは、ページの左端から 1 インチ、下端から 1 インチ、ページの中心方向に入った地点です。一例として、リスト 4 に円をレンダリングする PostScript を記載します。
リスト 4. 円をレンダリングする PostScript
上記によってページの (150, 150) の地点 (ページの左下隅に対する地点) に描画されるのは、角度 0 から角度 360 までの半径 10 の円弧 (つまり完全な円) です。乗算と同じように、オペランドが 1 つずつスタックにプッシュされてから arc 関数が呼び出され、この関数によってポップされたスタックの 5 つの項目によって円がレンダリングされます。
この 1 つの PostScript 命令によって、データ・ポイントからなるページを散布図で生成することができます。リスト 5 に、その場合の XSLT スタイルシート・テンプレートを記載します。
リスト 5. PostScript 散布図を生成する XSLT スタイルシート
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<apply-templates select="//point"/>
showpage
</xsl:template>
<xsl:template match="point">
<xsl:value-of select="x"/> <xsl:text> </xsl:text>
<xsl:value-of select="y"/> 5 0 360 arc
fill
</xsl:template>
</xsl:stylesheet>
|
上記では、<point x="30" y="50"/> という形の <point> 要素が含まれる XML 文書を取り、これらの要素を半径 5 のドットとしてページにプロットする PostScript 文書を生成して、一連の XML データ・ポイントを視覚的な散布図に変換します。最上位レベルのテンプレートの最後では、showpage を呼び出すことに注意してください。showpage はそれまでに描画されたすべての内容を印刷可能なページとしてレンダリングします。
レンダリング・ロジックの追加
前述の散布図では、XSLT を使用して PostScript を生成するという概念を最も単純な形で適用した場合を説明しています。この例での XSLT は XML 要素を取り、それぞれの要素を直接 PostScript コマンドに変換しているからです。この場合には、XML 要素のそれぞれに固有の座標情報がすでに含まれていますが、データに座標情報がないとなると (XML データとレンダリングされる PostScript との関係が弱い場合など)、事態は複雑になってきます。例えば散布図から 1 歩進んで、y 軸の値を表す一連の XML データ値から折れ線グラフをレンダリングするとします。この場合、XSLT が x 軸の値を計算する必要が出てきます (リスト 6 を参照)。
リスト 6. 一連の <temperature> 要素を PostScript の折れ線グラフに変換する XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:variable name="originY" select="100"/>
<xsl:variable name="originX" select="100"/>
<xsl:variable name="graphWidth" select="1000"/>
<xsl:variable name="graphHeight" select="1000"/>
<xsl:variable name="xDelta" select="$graphWidth / div count(//temperature)"/>
<xsl:variable name="tempRange" select="200"/>
<xsl:template match="/">
<xsl:apply-templates select="//temperature"/>
stroke
showpage
</xsl:template>
<xsl:template match="temperature">
<xsl:value-of select="ceiling($="$originX + (position() * $xDelta)"/>
<xsl:text> </xsl:text>
<xsl:value-of select="ceiling($originY + (@temp div / $tempRange *
$graphHeight))"/>
<xsl:if test="position() = 1">
moveto
<xsl:if>
<xsl:if test="position() != 1">
lineto
</xsl:else>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
|
このスタイルシートは、<temperature temp="30"/> という形の多数の <temperature> 要素が含まれる文書を取り、これらの要素を PostScript スタックに変換することによって温度の折れ線グラフをレンダリングします。スタイルシートが折れ線グラフの各点の x 値を計算するために使用しているのは、count() および position() という 2 つのXPath 関数です。点はページ全体で均等に分散させる必要があるため、このスタイルシートはまず、pageWidth 変数を文書内に含まれる <temperature> 要素の数で割ることによって、各点の間隔を計算します。その結果を保管するxDelta 変数は、各温度点の距離をピクセル数で表します。
root-level テンプレートは、散布図の例でのテンプレートと同じように機能し、<temperature> 要素に対する命令をレンダリングしてから、showpage を呼び出してグラフィック・バッファーのコンテンツをレンダリングします。temperature テンプレートは、xDelta 値に position() XPath 関数 (テンプレートに渡されるように選択された一連のノードにおける、現行ノードのインデックスを返す関数) から返された値を掛けることによって x 座標を計算します。さらに originX の値を加算して、グラフの左端に位置する点の x 座標を指定します。この値を出力し (値を出力プログラムの PostScript スタックにプッシュ)、次にグラフの高さに比例した温度である y 値を $originY + (@temp / $tempRange * $graphHeight) で計算して出力します。$originY によって、一番下の点がグラフの下端を越えないことを確実にします。そして、@temp / $tempRange * $graphHeight で温度の範囲に対する温度の比率を求め、その比率にグラフの高さを掛けて、温度に比例し、グラフの高さに収まる y 値を算出します。
XSLT を使用して作成された、行からなる PostScript ページのレンダリング
折れ線グラフでは、レンダリング・ロジックを XSLT に導入しました。ソース XML 文書に、グラフィックをレンダリングするために必要なすべての座標情報が含まれていなかったことから、スタイルシートは、提供された温度値からグラフ内の各折れ線の x 値と y 値を計算する必要があったためです。テキストが続くページをレンダリングするときにも、これと同じような問題があります。例えば、<line height="15">hello there</line> という形の <line> 要素からなる XML 文書があるとします。これらの要素をページにレンダリングするには、リスト 7 のスタイルシートが考えられます。
Lリスト 7. <line> 要素を PostScript に変換する XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:variable name="pageHeight" select="720"/>
<xsl:template match="/">
<xsl:apply-templates select="//line[1]">
<xsl:with-param name="y" select="0"/>
</xsl:apply-templates>
showpage
</xsl:template>
<xsl:template match="line">
<xsl:param name="y"/>
/Helvetica findfont <xsl:value-of select="@height"/> scalefont setfont
0 <xsl:value-of select="$pageHeight - $y"/> moveto
(<xsl:value-of select="text()") show
<xsl:apply-templates select="following-sibling::line[1]">
<xsl:with-param name="y" select="$y + @height"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
|
上記のスタイルシートには、多くのことが示されています。最終的な結果としては、<line height="20">some text</line> という形の一連の <line> 要素を取り、これらの行を 1 つずつページにレンダリングしていく PostScript コマンドを生成して、テキストの高さを指定する height 変数によって各行のフォント・サイズを変えられるようにしています。
このスタイルシートは、y 座標の値を次のテキスト行で維持するにはどうすればよいかという最大の問題を解決します。y 座標の値は、ページに追加される行ごとに増分値が異なる可能性があります。これは、折れ線グラフでの問題とは別に解決しなければならない問題です。折れ線グラフでは、データ・ポイントの間隔は均等なので、宣言型の式で簡単に配置することができます。しかしテキスト行の場合には、隣接する要素の間隔はそれぞれの要素に含まれるデータによって異なる可能性があります。そのため、スタイルシートはソース文書のデータ行を進むに従って累積される y 値を維持しなければなりません。この問題を解決するのは、パラメーターを取る再帰テンプレートです。呼び出したテンプレートに渡すことのできる引数を指定する <xsl:param> 要素は、XSLT を大幅に強化します。この場合で言うと、この要素によって y 座標の値を維持し、line テンプレートの次の再帰呼び出しには更新された y 値を渡すことが可能になります。
line テンプレートは y パラメーターを受け入れます。これはつまり、関数呼び出しに引数を渡すのと同じように、line テンプレートを呼び出すときには、このパラメーターの値を渡すことを意味します。このテンプレートは <line> 要素の height 属性を使用してテキスト行のフォント (つまり、行の高さ) を設定します。テンプレートの次の行が PostScript グラフィックのカーソルを x 値が 0 (つまり、ページの左端) および y 値が $pageHeight - $y (このような式になるのは PostScript のy 値がページ左下隅の原点に対する値であるため) に移動させる一方、データの <line> 要素が上から下に向かって並べられます。ここでの $y 値は、テンプレートの y パラメーターの値です。続いて、テンプレートは PostScript の show コマンドを使用して <line> 要素のテキストを表示します。PostScript テキストは、括弧内のストリングです。
テンプレートの最後の節は再帰を処理し、この同じテンプレートをデータ内の現行 <line> 要素の兄弟にあたる次の要素で呼び出し、この行の高さで更新した y 値を渡して、現行の行の直下にテキストの次の行を配置します。root-level テンプレートでも、このテンプレートを y パラメーターの値を 0 に設定して呼び出して、ページでの再帰フローを開始することに注意してください。
XSLT から PostScript へのレンダリング・ロジックの移動
折れ線グラフのスタイルシートには相当な量の計算が組み込まれていました。また、テキスト行をレンダリングするスタイルシートでも、大量のロジックを組み込んで y 座標を計算しています。これらのスタイルシートは、実際には XML データを手続き型プログラミング言語 (つまり、PostScript) に変換するコンパイラーです。そのため、レンダリング・ロジックのすべてを XSLT に組み入れる代わりに、レンダリング・ロジックを生成後の PostScript に含め、レンダリングの計算をコンパイル時ではなく、実行時に PostScript に行わせるという別の方法もあります。あるプログラミング言語から別のプログラミング言語にコンパイルするとき常に伴う選択は、どれだけの作業をコンパイラーで処理し、生成後の言語に実行時に行わせる作業をどれだけ残しておくかです。パフォーマンスを考慮すれば、通常はコンパイル時にできるだけ多くの作業を行うことを選択するはずです。PostScript はグラフィックとテキストのページを容易にレンダリングしやすくするために設計されています。一方、XSLT は XML データを別のフォーマットに変換するために設計されています。そこで、レンダリング・ロジックを XSLT から生成後の PostScript に移し、それぞれの言語にその得意とする処理を行わせれば、すべてが単純になります。
リスト 7 のテキストをレンダリングするスタイルシートを例に取ると、このスタイルシートでは y 座標の値を XSLT に維持します。リスト 8 は、それと同じことを生成後の PostScript で行う場合のスタイルシートです。
リスト 8. レンダリング計算をランタイム PostScript に移し、ページでテキスト行をレンダリングする代替 XSLT スタイルシート
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="/">
/y 72 11 mul def
/text_line
{
/height exch def
/text exch def
/y pop def
/Helvetica findfont height scalefont setfont
/y y height sub def
0 y moveto
text show
}
bind def
<xsl:apply-templates select="//line"/>
showpage
</xsl:template>
<xsl:template match="line">
(<xsl:value-of select="text()"/>) <xsl:value-of
select="@height"/> text_line
</xsl:template>
</xsl:stylesheet>
|
root-level テンプレートは /y という名前の PostScript 変数を定義し、ページの高さをポイント単位 (1 インチあたり 72 ポイント) で設定し、text_line という名前の関数を定義します。/text_line は PostScript 検索ディクショナリー内のエントリーを参照します。そのエントリーにバインドする関数の定義は中括弧 ({}) で囲まれたコンテンツに含まれ、最後の bind def がその関数の定義にエントリー名 text_line をバインドします。
text-line はローカル変数をスタックの値 (テキストの値および高さ) に設定します。つまり、text_line を呼び出すには text height text_line というフォーマットを使用するということです (例えば、(hello there) 20 text_line は、y の現行値に高さ 20 の「hello there」というテキストを配置します)。text_line は、指定された高さの値をフォントのスケール値として使用してフォントを設定します。次に、グラフィック・カーソルを x 座標が 0 (左端) で y 座標が指定された値の位置まで移動させ、指定されたテキスト・ストリングを表示します。続いて指定の高さを y 値に加算し、グローバル /y 変数をその合計値に設定して、text_line が呼び出されるごとに、作動中の y カウンターの値をページの下方向に向かって減らしていきます。
最後に、root-level テンプレートは 0 を開始 y 値としてスタックにプッシュし、ソース文書に含まれるすべての <line> 要素で line テンプレートを呼び出します。すべてのロジックは text_line PostScript 関数内に移されたことから、line テンプレート自体は単純化され、各 <line> 要素が直接、text_line PostScript 関数の呼び出しになります。
PostScript にすべてのレンダリング・ロジックを処理させることで、スタイルシートでは遙かに充実した言語を自由に、簡単に定義できます。例えば、スタイルシートで XHTML 全体を定義すれば、文書を XHTML として指定して、これらの文書を PostScript 文書にコンパイルすることが可能になります (コンパイル後の PostScript 文書は簡単に PDF に変換することができます)。
抽象化層
XHTML のような言語のスタイルシートを開発する際にも、スタイルシート・テンプレートのそれぞれに PostScript に関する情報をどれだけ持たせるかを決めなければなりません。つまり、テンプレート自体をレンダリングするために各テンプレートに含める PostScript ロジックをどれだけの量にするかです。1 つのソリューションは、各種の XHTML タグ (<h1>、<h2>、<h3> および <div>など) のテンプレートを作成し、各テンプレートがそれ自体をレンダリングする PostScript コマンド (text_line の呼び出しなど) を出力することです。このソリューションは、スタイルシートに定義された言語 (XHTML) と PostScript の言語を密接に結合させます。あるいは、リスト 8 のコードで <line> タグを直接 text_line PostScript 関数に変換したように、/h1、/h2/、/h3、/div などの関数をレンダリング後の PostScript に定義し、各 XHTML タグをそれに対応する関数の呼び出しに変換するという方法もあります。この場合、XSLT スタイルシートはまさに、利用可能な関数の観点から、PostScript に定義した言語をラップする XML ラッパー言語を定義することになり、生成された PostScript は XHTML の定義と密接に結合されます。いずれの場合のソリューションも、XHTML タグに PostScript の情報を大量に組み込むか、逆に (XHTML タグごとに PostScript 関数を作成することから) PostScript 関数に XHTML の情報を大量に組み込むことになるため、区別しておかなければならない 2 つの問題が一緒になってしまいます。2 つの問題の 1 つは、テキストが続くページをレンダリングするという問題で、これはページ・レンダリング言語である PostScript で処理したほうが適切です。そしてもう一方は XHTML 言語を定義する上での問題で、これは XML 方言を定義する XSLT で処理するのが適切です。
要するに、ここには別の抽象化層を加える余地があるということです。この抽象化層は、順番に適用される 2 つの XSLT スタイルシートとして実装します。最初のスタイルシートでは XHTML 言語のテンプレートを定義しますが、これらのテンプレートは PostScript コマンドや PostScript 関数呼び出しには変換されず、2 番目のスタイルシートが定義する中間 XML 方言に変換されます。2 番目のスタイルシートでは、XHTML と PostScript との間に中間言語を定義し、要素が続くページを定義するために必要なタグを作成します。この 2 番目のスタイルシートはこれらのタグを、テキストが続くページをレンダリングするために必要な PostScript 関数のコア・セットに変換します。この関数のコア・セットには、x および y カーソル値の管理、単語のラップ、ページネーション、そしてテキスト要素のレンダリングなどの振る舞いが含まれます。<line> 要素を text_line PostScript 関数呼び出しに変換するリスト 8 は 2 番目の中間スタイルシートの一例です。このスタイルシートでは PostScript 関数 text_line を囲むラッパー要素、<line> を定義しています。最初のスタイルシートはすべての XHTML タグのテンプレートを定義し、そのそれぞれのテンプレートが、2 番目のスタイルシートに定義されたタグ (<line> など) を出力します。一例として、リスト 9 に XHTML スタイルシートのサブセットを記載します。
リスト 9. XHTML のサブセットを中間 XML 方言に変換する XSLT スタイルシート
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="h1_height"/>
<xsl:param name="h2_height"/>
<xsl:template match="/">
<lines>
<xsl:apply-templates/>
</lines>
</xsl:template>
<xsl:template match="//h1">
<line height="$h1_height"><xsl:value-of select="text()"/></line>
</xsl:template>
<xsl:template match="//h2">
<line height="$h2_height"><xsl:value-of select="text()"/></line>
</xsl:template>
</xsl:stylesheet>
|
上記のスタイルシートでは、XHTML の小さなサブセットを定義し、「連続テキスト」生成言語である 2 番目のスタイルシートの中間ページ・レンダリング XML 方言に変換します。リスト 9 のスタイルシートは、h1_height と h2_height という 2 つのパラメーターを取り、呼び出し側が <h1> タグと <h2> タグのフォント・サイズを指定できるようにしています。例えば、呼び出し側は CSS スタイルシートを解析し、定義された CSS スタイルに基づく h1_height と h2_height の値を渡すという場合がありますが、//h1 および //h2 テンプレートは XHTML 固有の <h1> タグと <h2> タグを、高さが指定されたページのテキスト行を定義するだけの汎用 <line> タグに変換します。
このように原則を切り分け、最初に 2 つのスタイルシートを使うように促すことによって、XHTML を RTF などの別のフォーマットに変換するスタイルシートにしても、例えば RTF 文書をPostScript としてレンダリングするために RTF フォーマットを 2 番目のスタイルシートで定義された中間「連続ページ」方言に変換するスタイルシートにしても、再利用の可能性が生まれてきます。レンダリング・ロジックを PostScript 自体に保持させることで、XSLT スタイルシートでソース XML 方言を自由に定義し、ターゲットとする機能の役目 (PostScript レンダリング・ロジック) と言語定義 (XSLT スタイルシート) を分離させることが可能になります。
まとめ
XSLT を言語コンパイラーとして捉えることはあまりありませんが、あるデータ構造から別のデータ構造への変換を定義するという点では、XSLT はまさにコンパイラーです。そしてコンパイラーとしての XSLT スタイルシートはソース文書の言語を暗黙的に定義します。そのため、XHTML と PostScript、あるいは XML と Java というように関連性のない言語を結び付け、ターゲット言語への段階的な変換によって文書を別のフォーマットにレンダリングするために XSLT を使用することができます。
参考文献 学ぶために
製品や技術を入手するために
- ps2pdf: この PostScript と PDF 間のコンバーターがどのように機能するかを確認してください。
- Preview: このイメージおよび PDF (Portable Document Format) 文書の表示用 Mac OS X アプリケーションを試してみてください。
- IBM 製品評価用の試用版ソフトウェア: developerWorks から直接ダウンロードできるトライアル・ソフトウェアで、次のプロジェクトを構築してください。DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® によるアプリケーション開発ツールおよびミドルウェア製品のトライアル版が揃っています。
議論するために
著者について  | 
|  | Jake Miles は Conde Nast のシニア開発者であり、現在は Java、PHP、Adobe Flex、そして JavaScript を使って Facebook と Myspace、そして OpenSocial のアプリケーションに関する作業に従事しています。彼はプロの開発者として 10 年間働いており、しかも 10 歳の時から貪欲な学生であり、さまざまなものをいじってきました。彼はボランティアの教師もしています。 |
記事の評価
|