LoginSignup
1
2

More than 3 years have passed since last update.

Java + Apache Batik で SVG のパスデータ (path 要素の d 属性) を描画する

Last updated at Posted at 2020-10-24

概要

  • Apache Batik の Parser モジュールで SVG のパスデータ (path 要素の d 属性) フォーマットを解析して Shape オブジェクトを生成して描画処理をする

Apache Batik の Parser モジュール

Apache Batik の Parser モジュールは SVG を解析できる。
ただ、ドキュメントには Parser モジュールは一般的な開発には使うことを想定していないと書かれている (そのわりにはドキュメントがそれなりにちゃんと書かれている)。

Architecture overview

Finally, the Low level modules are used internally by the core modules to accomplish their work. These modules are not typically used by developers directly.

Parser module

SVG has a number of microsyntaxes that are used within attribute values, such as the transform attribute on SVGTransformable elements, and the path data d attribute on path elements. Since these are not trivial to parse, this functionality has been factored out into a separate package that can be used by other SVG-processing applications if needed.

Apache Batik の Parser モジュールを導入する build.gradle

Gradle 用の build.gradle を用意。

plugins {
  id 'java'
}

repositories {
  mavenCentral()
}

dependencies {
  // Apache Batik の Parser モジュールを導入
  // Batik SVG microsyntax parser
  // https://mvnrepository.com/artifact/org.apache.xmlgraphics/batik-parser
  implementation 'org.apache.xmlgraphics:batik-parser:1.13'
}

// サンプルコード PathDataDraw を実行するタスクを定義
task runPathDataDraw(type: JavaExec) {
  main = 'PathDataDraw'
  classpath = sourceSets.main.runtimeClasspath
}

// サンプルコード SVGPathDataDraw を実行するタスクを定義
task runSVGPathDataDraw(type: JavaExec) {
  main = 'SVGPathDataDraw'
  classpath = sourceSets.main.runtimeClasspath
}

パスデータを描画するサンプルコード

パスデータを文字列で用意しておいて、Apache Batik Parser で Shape オブジェクトを生成する。

import org.apache.batik.parser.AWTPathProducer;
import org.apache.batik.parser.PathParser;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class PathDataDraw {

  public static void main(String[] args) throws IOException {

    // 描画先の画像オブジェクト
    BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = image.createGraphics();

    // 背景を白色で塗りつぶす
    g.setBackground(Color.WHITE);
    g.clearRect(0, 0, 500, 500);

    // 描画用のペン
    g.setStroke(new BasicStroke(5.0f));

    // 描画するパスデータのリスト
    // パスデータは SVG の path 要素の d 属性に指定するものと同等の文字列
    // path 要素の例: <path d="M 0,0 h 500 v 500 h -500 z" />
    String[] openedPathList = {
      "M 0,0 h 500 v 500 h -500 z", // 画像サイズと同じ正方形
      "M 30 30 C 40 140, 80 140, 100 110", // ベジェ曲線
      "M 30 80 C 120 10, 195 10, 285 80 S 450 150, 480 80", // ベジェ曲線
      "M 10 80 Q 52.5 10, 95 80 T 180 80", // ベジェ曲線
      "A 30 50 -45 0 1 215.1 109.9", // 円弧
    };
    // パスデータを描画
    g.setPaint(Color.RED);
    for (String path : openedPathList) {
      // パスデータを Shape オブジェクトに変換
      Shape shape = getShape(path);
      // パスデータを線として描画
      g.draw(shape);
    }

    // 描画するパスデータのリスト
    String[] closedPathList = {
      "M 100,200 h 200 v 100 h -200 z", // 長方形
      "M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z", // ハート
    };
    // パスデータを描画する
    for (String path : closedPathList) {
      // パスデータを Shape オブジェクトに変換
      Shape shape = getShape(path);
      // パスデータを面として描画 (塗りつぶし)
      g.setPaint(Color.GREEN);
      g.fill(shape);
      // パスデータを線として描画
      g.setPaint(Color.BLUE);
      g.draw(shape);
    }

    // 画像を PNG ファイルとして出力
    ImageIO.write(image, "png", new File("output.png"));
  }

  /**
   * パスデータを Shape オブジェクトに変換する。
   * @param d path 要素の d 属性に指定するものと同等の文字列
   * @return 形状を表すオブジェクト
   */
  private static Shape getShape(String d) {

    // d 属性値を解析するパーサー
    PathParser parser = new PathParser();

    // 解析結果を受け取って Shape を生成するオブジェクト
    // AWTPathProducer は PathHandler インターフェースを実装している
    AWTPathProducer producer = new AWTPathProducer();

    // パーサーに PathHandler を設定
    parser.setPathHandler(producer);

    // 解析処理を実施
    parser.parse(d);

    // Shape オブジェクトwp取得
    Shape shape = producer.getShape();

    return shape;
  }
}

SVG ファイルから path 要素の d 属性を抽出してパスデータを描画するサンプル

SVG ファイルから path 要素の d 属性を抽出し、そのパスデータを Apache Batik Parser で Shape オブジェクトに変換する。

import org.apache.batik.parser.AWTPathProducer;
import org.apache.batik.parser.PathParser;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Random;

public class SVGPathDataDraw {

  public static void main(String[] args) throws IOException, XPathExpressionException, ParserConfigurationException, SAXException {

    // SVG ファイルを読み込む
    String svgFile = "input.svg";
    Document doc;
    try (FileInputStream is = new FileInputStream(Paths.get(svgFile).toFile())) {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder = factory.newDocumentBuilder();
      doc = builder.parse(is);
    }

    // 描画先のオブジェクトを用意
    BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = image.createGraphics();

    Random r = new Random(); // 乱数発生機

    // SVG 文書から path 要素の d 属性を抜き出して描画
    XPath xpath = XPathFactory.newInstance().newXPath();
    NodeList nodelist = (NodeList) xpath.evaluate("//path/@d", doc, XPathConstants.NODESET);
    for (int i = 0; i < nodelist.getLength(); i++) {
      // d 属性の値を取得
      Attr attr = (Attr) nodelist.item(i);
      // d 属性の値を Shape オブジェクトに変換
      Shape shape = getShape(attr.getValue());
      // ランダムで描画色を決める
      g.setColor(new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256)));
      // パスデータを描画
      g.draw(shape);
    }

    // 画像を PNG ファイルとして出力
    ImageIO.write(image, "png", new File("output.png"));
  }

  /**
   * パスデータを Shape オブジェクトに変換する。
   * @param d path 要素の d 属性に指定するものと同等の文字列
   * @return 形状を表すオブジェクト
   */
  private static Shape getShape(String d) {
    PathParser parser = new PathParser();
    AWTPathProducer producer = new AWTPathProducer();
    parser.setPathHandler(producer);
    parser.parse(d);
    return producer.getShape();
  }
}

参考資料

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2