|
SVG (Scalable Vector Graphics) は、広範な領域、アプリケーションで約束されたグラフィック フォーマットとして登場しており、これとJavaを結びつけることが重要です。
このページでは、SVGジェネレータとして参照される、BatikのSVGGraphics2D が、どのようにしてこれを可能にしているかを説明します。これは3部構成に分かれます:
|
Javaプラットフォームでは、全てのレンダリングはjava.awt.Graphics2D 抽象クラスを通じて行われます。これはdrawRect , fillRect , あるいはdrawString といったメソッドを提供します。モニタやプリンタといった、それぞれの出力のタイプに応じて、この抽象クラスのスペシャライズされた実装があります。
SVGGraphics2D は、このインターフェースで、スクリーンやプリンタに描画するかわりに、SVGコンテンツを生成するための、新しい実装です。
SVGGraphics2D は以下のものを提供しています:
- アプリケーションがグラフィックをSVGフォーマットで書き出すことが可能になります。
- グラフィックコードをSVGにエクスポートするために、コードを修正する必要はありません。
- ユーザーがDOM APIを使って生成されたドキュメントを操作することを可能にします。
上記の図は、ジェネレータがどのようにDOM APIと連携するかを示しています。W3Cは、XMLコンテンツをJavaプログラミング言語オブジェクトで表現するAPIを定義しています。このAPIはプログラマーがメモリ上のXMLコンテンツを操作し、生成し、また変更することを可能にしています。DOM API には、Document ,
Element , Attr といった、Javaプログラミング言語でXMLドキュメント、要素、属性をモデル化するインターフェースが含まれています。
このジェネレータは、SVGGraphics2D インスタンスによってなされるレンダリング呼び出しに該当するSVGコンテンツを表す、DOMオブジェクトのツリーを管理します。言い換えれば、プログラムがSVGGraphics2D インスタンスでfillRect のようなレンダリングメソッドを呼び出すたびに、そのSVGに等しいものを表している新しいDOMのオブジェクトが、DOM ツリーに追加されます(たとえば、fillRect メソッドが呼び出されると、<rect> 要素が追加されます)。
このジェネレータを使用するプログラマーは、これでDOMツリーにアクセスして、以降のセクションで見るように、これを操作したり、出力ストリームにコンテンツを直接書き出すことができます。
|
先のセクションの図から、SVGGraphics2D のインスタンスからSVGコンテンツ(DOMツリー)をビルドするためには、DOMのDocument クラスのインスタンスが必要であるということが分かりました。このDOMツリーは、SVGドキュメントのメモリ中の表現で、DOM APIを用いて操作したり、任意のjava.io.Writer を用いてストリーム出力したりといったことを可能にします。
以下の抜粋コードの例は、SVGコンテンツがどのようにJavaグラフィックスから生成されるかを示しています。
| | | |
import java.awt.Rectangle;
import java.awt.Graphics2D;
import java.awt.Color;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.IOException;
import org.apache.batik.svggen.SVGGraphics2D;
import org.apache.batik.dom.GenericDOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DOMImplementation;
public class TestSVGGen {
public void paint(Graphics2D g2d) {
g2d.setPaint(Color.red);
g2d.fill(new Rectangle(10, 10, 100, 100));
}
public static void main(String [] args) throws IOException {
// Get a DOMImplementation
DOMImplementation domImpl =
GenericDOMImplementation.getDOMImplementation();
// Create an instance of org.w3c.dom.Document
Document document = domImpl.createDocument(null, "svg", null);
// Create an instance of the SVG Generator
SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
// Ask the test to render into the SVG Graphics2D implementation
TestSVGGen test = new TestSVGGen();
test.paint(svgGenerator);
// Finally, stream out SVG to the standard output using UTF-8
// character to byte encoding
boolean useCSS = true; // we want to use CSS style attribute
Writer out = new OutputStreamWriter(System.out, "UTF-8");
svgGenerator.stream(out, useCSS);
}
} | | | | |
ここで我々には、SVG コンテンツを TestSVGGen のインスタンスから生成するのは、3ステップのプロセスであることが分かります:
1. ジェネレータがXMLコンテンツをビルドするために用いられる org.w3c.dom.Document のインスタンスを生成する; Document のインスタンスを用いて、SVGジェネレータを生成します。
| | | |
// Get a DOMImplementation
DOMImplementation domImpl =
GenericDOMImplementation.getDOMImplementation();
// Create an instance of org.w3c.dom.Document
Document document = domImpl.createDocument(null, "svg", null);
// Create an instance of the SVG Generator
SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
| | | | |
2. SVG ジェネレータからレンダリング コードを呼び出す. この例では、TestSVGGen の
paint メソッドを呼び出しています:
| | | |
// Ask the test to render into the SVG Graphics2D implementation
TestSVGGen test = new TestSVGGen();
test.paint(svgGenerator);
| | | | |
3. SVG コンテンツをストリーム出力する。SVG ジェネレータは、その内容を任意のjava.io.Writer にストリームする事ができます。この例では、コンテンツを標準出力ストリームにストリームしています:
| | | |
// Finally, stream out SVG to the standard output using UTF-8
// character to byte encoding
boolean useCSS = true; // we want to use CSS style attribute
Writer out = new OutputStreamWriter(System.out, "UTF-8");
svgGenerator.stream(out, useCSS);
| | | | |
SVGには、フィル カラーみたいな、プロパティを表現する、2つの方法があります: XMLの属性と、そしてCSSのインラインプロパティです。'useCss' パラメータによって、ユーザーはこのオプションを制御できます。
|
先のセクションでは、我々はただSVG生成プロセスが、SVG出力スタイルをXML属性にするか、それともCSSインラインプロパティにするかをカスタマイズできる、というのを見ただけでした。このセクションでは、より進んだカスタマイズの例をいくつかお話ししましょう。
SVGGraphics2D を生成する代わりに、単純にDocument を使用してSVG要素の生成を行うことによって、SVGGeneratorContext のインスタンスを使用するコンストラクタを用いることが出来ます。あなた自身のSVGGeneratorContext のインスタンスを用いることによって、あなたはより発展したカスタマイズを行うことができるでしょう。以下では、カスタマイズのできる、さまざまな例を見ることができます。
| | | | 生成されるSVGファイル中に自分のコメントを作り出す | | | | |
| | | | 埋め込みSVGフォントを生成されるSVGファイル中で使用する | | | | |
Graphics2D クラスで提供されているdrawImage メソッドのひとつを呼び出すたびに、あなたのイメージの標準的な表現が、生成されたSVGファイルから届く場所に生成されます。たとえば、base64エンコーディングが、デフォルトでは生成されてそのSVGファイル中に埋め込まれることになります。そうするかわりに、あなたはイメージを既定のディレクトリに、SVG仕様で要求されている2種類のラスタ フォーマットであるJPEGあるいはPNGとして、独立したファイルに書き出すこともできます。
あなたはSVGジェネレータで使用されているイメージハンドラを明示的に提供することで、デフォルトの振る舞いを変えることが出来ます。そうしたら、もう一度SVGGeneratorContext をこれに使ってみてください。以下の例では、全てのイメージがPNGフォーマットにコンバートされて"res/images"ディレクトリに書き出されます。
| | | |
SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory);
ImageHandler ihandler = new ImageHandlerPNGEncoder("res/images", null);
ctx.setImageHandler(ihandler);
SVGGraphics2D g2d = new SVGGraphics2D(ctx, false);
| | | | |
デフォルトイメージハンドラを使用すると、それぞれの1つ1つのdrawImage の呼び出しについて、イメージデータの新しいコピーとしてSVGファイルあるいは外部ファイルに出力されます。もしあなたが同じイメージを何度も使用していると、これは冗長なデータをたくさん含むSVGファイルになります。初期SVG DOMツリーの生成の間のわずかなパフォーマンスのペナルティを犠牲にすることで、あなたはイメージデータを再利用することもできます。このためには、以下に示すような、特別なイメージハンドラを使用します。
| | | |
SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory);
// Reuse our embedded base64-encoded image data
GenericImageHandler ihandler = new CachedImageHandlerBase64Encoder();
ctx.setGenericImageHandler(ihandler);
SVGGraphics2D g2d = new SVGGraphics2D(ctx, false);
| | | | |
イメージハンドラをキャッシュする事で、イメージデータを異なるSVGドキュメント間でも同じコピーを再利用することも可能になっています。イメージハンドラへの参照を保存しておいて、それをSVG DOMツリーを生成するSVGGraphics2D のインスタンスに渡します。以下の簡略化した例では、異なるSVGツリーが別々のSVGジェネレータの間で生成されうる例を示しています。共通のイメージは効率的に1回しか保存されていません。
| | | |
class MySVGGenerator {
// the image handler will write all images files to "res/images"
private static ImageHandler ihandler =
new CachedImageHandlerPNGEncoder("res/images", null);
public void generateSVG(JPanel myCanvas, OutputStream outStream) {
DOMImplementation domImpl =
GenericDOMImplementation.getDOMImplementation();
Document myFactory = domImpl.createDocument(null, "svg", null);
SVGGeneratorContext ctx =
SVGGeneratorContext.createDefault(myFactory);
ctx.setGenericImageHandler(ihandler);
SVGGraphics2D svgGenerator =
new SVGGraphics2D(ctx, false);
// create the SVG DOM tree
myCanvas.paintComponent(svgGenerator);
Writer out = new OutputStreamWriter(outStream, "UTF-8");
svgGenerator.stream(out, true);
}
} | | | | |
|
| | | | ペイント オブジェクトをSVG要素の変形に拡張する | | | | |
SVGGraphics2D はSVG要素から一般的なJava 2Dオブジェクトを生成することが出来ます。しかし、あなたは時にはあなた自身のクラス、たとえばJava 2D java.awt.Paint インターフェースの実装などを持っていることがあります。このような場合、あなたはExtensionHandler を書いて、自分のSVGGeneratorContext を使用する必要があるかもしれません。
以下の例では、org.apache.batik.ext.awt.LinearGradientPaint という名前の、Batik のjava.awt.Paint インターフェースの実装を変形できるようなExtensionHandler の最初のドラフトを定義しています。
| | | |
class SubExtensionHandler extends DefaultExtensionHandler
{
public SVGPaintDescriptor handlePaint(Paint paint,
SVGGeneratorContext generatorCtx)
{
if (paint instanceof LinearGradientPaint) {
LinearGradientPaint gradient = (LinearGradientPaint)paint;
String id = generatorCtx.getIDGenerator().generateID("gradient");
Element grad = generatorCtx.getDOMFactory().
createElementNS(SVGSyntax.SVG_NAMESPACE_URI,
SVGSyntax.SVG_LINEAR_GRADIENT_TAG);
grad.setAttributeNS(null, SVGSyntax.SVG_ID_ATTRIBUTE, ref);
grad.setAttributeNS(null,
SVGSyntax.SVG_GRADIENT_UNITS_ATTRIBUTE,
SVGSyntax.SVG_USER_SPACE_ON_USE_VALUE);
Point2D pt = gradient.getStartPoint();
grad.setAttributeNS(null, "x1", pt.getX());
grad.setAttributeNS(null, "y1", pt.getY());
pt = gradient.getEndPoint();
grad.setAttributeNS(null, "x2", pt.getX());
grad.setAttributeNS(null, "y2", pt.getY());
switch (gradient.getCycleMethod()) {
case MultipleGradientPaint.REFLECT:
grad.setAttributeNS(null,
SVGSyntax.SVG_SPREAD_METHOD_ATTRIBUTE,
SVGSyntax.SVG_REFLECT_VALUE);
break;
case MultipleGradientPaint.REPEAT:
grad.setAttributeNS(null,
SVGSyntax.SVG_SPREAD_METHOD_ATTRIBUTE,
SVGSyntax.SVG_REPEAT_VALUE);
break;
// pad is the default...
}
// here we should write the transform of the gradient
// in the transform attribute.
// here we should write the stops of the gradients as
// children elements.
return new SVGPaintDescriptor("url(#"+ref+")",
SVGSyntax.SVG_OPAQUE_VALUE, grad);
} else
return null; // let the default mechanism do its job
}
}
| | | | |
そうしたら、これをSVGGeneratorContext 上に、 setExtensionHandler メソッドを用いてセットします。
| | | |
SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory);
ctx.setExtensionHandler(new SubExtensionHandler());
SVGGraphics2D g2d = new SVGGraphics2D(ctx); | | | | |
|
|
|
|