CSS調整

2017年12月10日

batikでsvgをpng化

昔はsvgなんてありえへん、ベクター画像は印刷用!eps!eps!って感じだったけれど、最近の自分はsvg無しでは生きていけなくなってしまいました
特にインラインのsvg
Angularみたいなどんな要素でもコンポーネント化できる環境だと(最終的なソースのでかさ以外は)何も気にせずsvgの内部をTSでいじり倒せるし
(canvasでええやんってのは置いておきます)

batik自体はまあ、今更感はある気がしますが、試してみます

まずは依存関係 pom.xml

<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-transcoder</artifactId>
    <version>1.9.1</version>
</dependency>
<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-codec</artifactId>
    <version>1.9.1</version>
</dependency>

svgの文字列を読み込む場合

val svgStr = ...
val doc = SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
   .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, StringReader(svgStr))


createDocumentの第2引数がReaderやInputStreamを読み込めるので、

svgのファイルを読み込む場合

val doc = File("image.svg").inputStream().use {
 SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
  .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, it)
}


生成されるのはorg.w3c.dom.Documentなので、

DOM操作は容易かと

val svgStr = ...
val doc = SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
 .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, StringReader(svgStr)).apply {
 getElementById("title").textContent = "タイトル"
}


読み込んだsvgをネストしたい場合

はcloneして追加して適用する
val svgStr = ...
val doc = SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
 .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, StringReader(svgStr)).apply {
 
 val svg2Str = ...
 val clone = SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
  .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, StringReader(svg2Str))
  .documentElement
  .cloneNode(true)
 adoptNode(clone)
 documentElement.appendChild(clone)
}


pngにはき出すには

TranscoderInputとTranscoderOutputをPNGTranscoderに与えます
例ではTranscodeする際に、画像サイズを指定
val transOut = ByteArrayOutputStream()
PNGTranscoder().apply {
 val rect = Rectangle(0, 0, 500, 500)
 addTranscodingHint(PNGTranscoder.KEY_WIDTH, rect.width.toFloat())
 addTranscodingHint(PNGTranscoder.KEY_HEIGHT, rect.height.toFloat())
 transcode(TranscoderInput(doc), TranscoderOutput(transOut))
}


TranscoderOutputはWriterやOutputStreamを受け取るので、

ファイル出力するなら以下のような感じ

File("image.png").outputStream().use {
 PNGTranscoder().apply {
  val rect = Rectangle(0, 0, 500, 500)
  addTranscodingHint(PNGTranscoder.KEY_WIDTH, rect.width.toFloat())
  addTranscodingHint(PNGTranscoder.KEY_HEIGHT, rect.height.toFloat())
  transcode(TranscoderInput(doc), TranscoderOutput(it))
 }
}


ちなみに

Transcodeではなく出来上がったsvgをStringでほしいなら

batikは関係なくて、普通にjavax.xml周りを使って以下とか
val xmlStr = StringWriter().use {
 TransformerFactory.newInstance().newTransformer().apply { 
  setOutputProperty(OutputKeys.INDENT, "yes")
  setOutputProperty(OutputKeys.METHOD, "xml")
  setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2")
  transform(DOMSource(doc), StreamResult(it))
 }
 it.toString()
}


便利な世の中ですねぇ~

いじょ