Help us understand the problem. What is going on with this article?

Java Graphics2D と各種ライブラリでベクターグラフィックス画像 (PDF,SVG,PPT,EPS,SWF) を出力する

More than 1 year has passed since last update.

概要

  • Java 標準ライブラリの Graphics2D クラスと外部ライブラリを使用して、ベクターグラフィックス画像 (PDF,SVG,PPT,EPS,SWF) を出力する
  • 具体的な描画処理は Graphics2D を使用して共通化する
  • 各種ベクター形式画像フォーマットへの出力は外部ライブラリを使用する
  • 今回のプログラムは Graphics2D を使って描画しているような振る舞いをするが、実際には各種フォーマットに対応した Graphics2D のサブクラスが処理をしている
  • 確認用に PNG (Portable Network Graphics) 画像も出力する

使用ライブラリ

今回の動作環境

  • macOS Mojave
  • OpenJDK 11.0.2
  • Apache Maven 3.6.1

ソースコード

ソースコード一覧

  • pom.xml: Maven のビルド用設定ファイル
  • App.java: 各種フォーマットへ出力するプログラム
├── pom.xml
└── src
    └── main
        └── java
            └── com
                └── example
                    └── App.java

pom.xml: Maven のビルド用設定ファイル

  • 各種外部ライブラリを dependencies に追加している
  • 依存ライブラリを含めた実行可能な JAR ファイルを生成するため Maven Assembly Plugin を導入している
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>my-app</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <!-- For PDF -->
    <!-- https://mvnrepository.com/artifact/com.orsonpdf/orsonpdf -->
    <dependency>
      <groupId>com.orsonpdf</groupId>
      <artifactId>orsonpdf</artifactId>
      <version>1.9</version>
    </dependency>
    <!-- For SVG -->
    <!-- https://mvnrepository.com/artifact/org.apache.xmlgraphics/batik-all -->
    <dependency>
      <groupId>org.apache.xmlgraphics</groupId>
      <artifactId>batik-all</artifactId>
      <version>1.11</version>
      <type>pom</type>
    </dependency>
    <!-- For PPT -->
    <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-scratchpad -->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-scratchpad</artifactId>
      <version>4.1.0</version>
    </dependency>
    <!-- For EPS -->
    <!-- https://mvnrepository.com/artifact/de.erichseifert.vectorgraphics2d/VectorGraphics2D -->
    <dependency>
      <groupId>de.erichseifert.vectorgraphics2d</groupId>
      <artifactId>VectorGraphics2D</artifactId>
      <version>0.13</version>
    </dependency>
    <!-- For SWF -->
    <!-- https://mvnrepository.com/artifact/org.freehep/freehep-graphicsio-swf -->
    <dependency>
      <groupId>org.freehep</groupId>
      <artifactId>freehep-graphicsio-swf</artifactId>
      <version>2.4</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- http://maven.apache.org/plugins/maven-assembly-plugin/ -->
      <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <version>3.1.1</version>
      <executions>
        <execution>
        <phase>package</phase>
        <goals>
          <goal>single</goal>
        </goals>
        </execution>
      </executions>
      <configuration>
        <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
        <manifest>
          <mainClass>com.example.App</mainClass>
        </manifest>
        </archive>
      </configuration>
      </plugin>
    </plugins>
  </build>

</project>

App.java: 各種フォーマットへ出力するプログラム

package com.example;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.File;
import java.io.FileOutputStream;
import javax.imageio.ImageIO;
import org.w3c.dom.DOMImplementation;

// PDF
import com.orsonpdf.Page;
import com.orsonpdf.PDFDocument;
import com.orsonpdf.PDFGraphics2D;

// SVG
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.svggen.SVGGraphics2D;

// PPT
import org.apache.poi.hslf.model.PPGraphics2D;
import org.apache.poi.hslf.usermodel.HSLFGroupShape;
import org.apache.poi.hslf.usermodel.HSLFSlide;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;

// EPS
import de.erichseifert.vectorgraphics2d.Document;
import de.erichseifert.vectorgraphics2d.Processor;
import de.erichseifert.vectorgraphics2d.Processors;
import de.erichseifert.vectorgraphics2d.VectorGraphics2D;
import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence;
import de.erichseifert.vectorgraphics2d.util.PageSize;

// SWF
import org.freehep.graphicsio.swf.SWFGraphics2D;

public class App {

  public static void main(String[] args) throws Exception {
    createPNG();
    createPDF();
    createSVG();
    createPPT();
    createEPS();
    createSWF();
  }

  /**
   * 描画します。
   *
   * @param g Graphics2D オブジェクト
   * @param w 描画範囲の横幅
   * @param h 描画範囲の縦幅
   */
  private static void draw(Graphics2D g, double w, double h) {
    // 線の太さ
    g.setStroke(new BasicStroke((float) (w + h) / 20.0f));
    // 背景
    g.setPaint(Color.LIGHT_GRAY);
    g.fill(new Rectangle2D.Double(0, 0, w, h));
    // 枠線
    g.setPaint(Color.BLUE);
    g.draw(new Rectangle2D.Double(0, 0, w, h));
    // 円
    g.setPaint(Color.RED);
    g.fill(new Ellipse2D.Double(w / 4, h / 4, w / 2, h / 2));
    // 線
    g.setPaint(new Color(255, 255, 0, 100));
    Line2D line = new Line2D.Double(w / 8, h / 8, w / 2, h / 2);
    g.draw(line);
  }

  /**
   * PNG (Portable Network Graphics) ファイルを出力します。
   */
  private static void createPNG() throws IOException {
    int w = 297 * 2;
    int h = 210 * 2;
    BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = image.createGraphics();
    draw(g, w, h);
    ImageIO.write(image, "png", new File("png.png"));
  }

  /**
   * PDF (Portable Document Format) ファイルを出力します。
   */
  private static void createPDF() throws Exception {
    // 1 inch = 25.4mm = 72pt
    double w = 297 * 72 / 25.4;
    double h = 210 * 72 / 25.4;
    PDFDocument pdfDoc = new PDFDocument();
    Page page = pdfDoc.createPage(new Rectangle2D.Double(0, 0, w, h));
    PDFGraphics2D g = page.getGraphics2D();
    draw(g, w, h);
    pdfDoc.writeToFile(new File("pdf.pdf"));
  }

  /**
   * SVG (Scalable Vector Graphics) ファイルを出力します。
   */
  private static void createSVG() throws IOException {
    int w = 297 * 2;
    int h = 210 * 2;
    DOMImplementation domImpl = SVGDOMImplementation.getDOMImplementation();
    String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
    org.w3c.dom.Document doc = domImpl.createDocument(svgNS, "svg", null);
    SVGGraphics2D g = new SVGGraphics2D(doc);
    g.setSVGCanvasSize(new Dimension(w, h));
    draw(g, w, h);
    g.stream("svg.svg");
  }

  /**
   * PPT (PowerPoint) ファイルを出力します。
   */
  private static void createPPT() throws IOException {
    HSLFSlideShow ppt = new HSLFSlideShow();
    Dimension pageSize = ppt.getPageSize();
    double w = 297;
    double h = 210;
    double coef = Math.min(pageSize.getWidth() / w, pageSize.getHeight() / h);
    w = w * coef;
    h = h * coef;
    HSLFSlide slide = ppt.createSlide();
    HSLFGroupShape group = slide.createGroup();
    Rectangle2D bounds = group.getAnchor();
    bounds.setRect(0, 0, w, h);
    PPGraphics2D g = new PPGraphics2D(group);
    draw(g, w, h);
    try (FileOutputStream out = new FileOutputStream("ppt.ppt")) {
      ppt.write(out);
    }
  }

  /**
   * EPS (Encapsulated PostScript) ファイルを出力します。
   */
  private static void createEPS() throws IOException {
    int w = 297 * 2;
    int h = 210 * 2;
    VectorGraphics2D g = new VectorGraphics2D();
    draw(g, w, h);
    CommandSequence commands = g.getCommands();
    Processor processor = Processors.get("eps");
    PageSize size = new PageSize(w, h);
    Document doc = processor.getDocument(commands, size);
    doc.writeTo(new FileOutputStream("eps.eps"));
  }

  /**
   * SWF (Small Web Format / Shockwave Flash) ファイルを出力します。
   */
  private static void createSWF() throws IOException {
    int w = 297 * 2;
    int h = 210 * 2;
    SWFGraphics2D g = new SWFGraphics2D(new File("swf.swf"), new Dimension(w, h));
    g.writeHeader();
    g.startExport();
    draw(g, w, h);
    g.endExport();
    g.dispose();
  }
}

プログラムをビルドして実行

Maven で実行可能な JAR ファイルを生成する。

$ mvn package

java コマンドで JAR ファイルを指定して実行する。

$ java -jar target/my-app-1.0-SNAPSHOT-jar-with-dependencies.jar

これで PNG, PDF, SVG, PPT, EPS, SWF の6種類のファイルが出力できる。

出力された各種ベクターグラフィックス画像ファイル

PNG (Portable Network Graphics)

PNG はベクター形式の画像フォーマットではないが比較用に載せておく。

PNG ファイルの情報をコマンドで確認する。

$ file png.png
png.png: PNG image data, 594 x 420, 8-bit/color RGBA, non-interlaced

$ pngcheck png.png
OK: png.png (594x420, 32-bit RGB+alpha, non-interlaced, 99.3%).

画像は macOS Mojave のプレビューアプリで閲覧したもの。

png.png

PDF (Portable Document Format)

PDF ファイルの情報をコマンドで確認する。

$ file pdf.pdf
pdf.pdf: PDF document, version 1.4

$ pdfinfo pdf.pdf
Producer:       OrsonPDF 1.7
CreationDate:   Sun Jul 28 15:24:27 2019
ModDate:        Sun Jul 28 15:24:27 2019
Tagged:         no
Form:           none
Pages:          1
Encrypted:      no
Page size:      841.89 x 595.276 pts (A4) (rotated 0 degrees)
File size:      1138 bytes
Optimized:      no
PDF version:    1.4

画像は Adobe Acrobat Reader DC で閲覧したもの。

pdf.png

SVG (Scalable Vector Graphics)

SVG ファイルの情報をコマンドで確認する。

$ file svg.svg
svg.svg: SVG Scalable Vector Graphics image

$ identify svg.svg
svg.svg SVG 594x420 594x420+0+0 16-bit sRGB 1379B 0.000u 0:00.012

画像は Inkscape で閲覧したもの。ベクターデータのオブジェクトが確認できる。

svg.png

SVG は XML 文書なので中身がテキストデータになっており確認しやすい。生成された SVG ファイルの中身をここに載せておく。

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
          'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg stroke-dasharray="none" shape-rendering="auto" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" width="594" text-rendering="auto" fill-opacity="1" contentScriptType="text/ecmascript" color-interpolation="auto" color-rendering="auto" preserveAspectRatio="xMidYMid meet" font-size="12px" fill="black" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="black" image-rendering="auto" stroke-miterlimit="10" zoomAndPan="magnify" version="1.0" stroke-linecap="square" stroke-linejoin="miter" contentStyleType="text/css" font-style="normal" height="420" stroke-width="1" stroke-dashoffset="0" font-weight="normal" stroke-opacity="1"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
  /><g
  ><g fill="silver" stroke-width="50.7" stroke="silver"
    ><rect x="0" width="594" height="420" y="0" stroke="none"
      /><rect fill="none" x="0" width="594" height="420" y="0" stroke="blue"
      /><ellipse rx="148.5" fill="red" ry="105" cx="297" cy="210" stroke="none"
    /></g
    ><g fill="rgb(255,255,0)" fill-opacity="0.3922" stroke-width="50.7" stroke-opacity="0.3922" stroke="rgb(255,255,0)"
    ><line y2="210" fill="none" x1="74.25" x2="297" y1="52.5"
    /></g
  ></g
></svg
>

PPT (PowerPoint)

PPT ファイルの情報をコマンドで確認する。

$ file ppt.ppt
ppt.ppt: Composite Document File V2 Document, Little Endian, Os: Windows, Version 5.1, Code page: 1252, Title: PowerPoint Presentation, Revision Number: 0, Name of Creating Application: Microsoft PowerPoint, Last Saved Time/Date: Mon Apr  7 20:47:14 2003, Number of Words: 0

画像は Keynote で閲覧したもの。ベクターデータのオブジェクトが確認できる。

ppt.png

EPS (Encapsulated PostScript)

EPS ファイルの情報をコマンドで確認する。

$ file eps.eps
eps.eps: PostScript document text conforming DSC level 3.0, type EPS, Level 3

画像は macOS Mojave のプレビューアプリで閲覧したもの。ベクターデータのオブジェクトが確認できる。

eps.png

SWF (Small Web Format / Shockwave Flash)

SWF ファイルの情報をコマンドで確認する。

$ file swf.swf
swf.swf: Macromedia Flash data (compressed), version 8

画像は Elmedia Video Player で閲覧したもの。

swf.png

参考資料

OrsonPDF

Apache Batik

Apache POI

VectorGraphics2D

FreeHEP VectorGraphics

niwasawa
迷子になりがちな地図・位置情報系プログラマ。
http://niwasawa.hatenablog.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away