トップ地図アプリMap4 > SVGファイルをPNGファイルに変換する

SVGファイルをPNGファイルに変換する

はじめに

地図アプリの自作は10年近くなる。開発言語は Python、C++、C#、Java/Android Java という変遷をたどってきた。

地図には数十~数百という様々なアイコンが表示される。 PNGファイルの場合、上記いずれの言語でもそのまま描画できる。 しかし、アイコン画像ファイルはSVGファイルで入手する方が精度高く任意の大きさに拡大・縮小できる。

開発言語によっては SVGファイルを直接表示できるが、Android Java では事前に SVGファイルを PNG ファイルに 変換する必要がある。

当初は、Windowsタブレットでの地図表示であったため、タイル画像のサイズは 256x256画素であった。 しかし、現在のスマホでは、768x768画素程度が適当である。 これにともない、アイコン画像のサイズも縦横3倍が適当な大きさになる。

256x256画素タイル用アイコンを読み込んで、縦横3倍に拡大するよりも、 縦横が3倍のアイコンを表示するほうが綺麗になる。

同じ地図を Androidタブレットでも表示する。 この場合は、タイル画像のサイズを 256x256画素か 512x512画素とする。 後者の場合、縦横2倍のアイコンファイルがほしい。

SVGからPNGへの変換は ImageMagickだったか Inkscape だったかをプログラムに組み込んだこともあるが、 最近は、SVGファイルを手作業で修正して、それを Internet Explorer で表示して、 PNGファイルでセーブする方法を採っている。 数が少ない場合はこれでよいが、一挙に、数十~数百の SVGファイルについて、 サイズ2倍と3倍のPNGファイルを作るのは容易ではない。 何か、もう少し、楽な方法を考えたい。

アイコンのサイズは同じではない。256x256画素タイル用のアイコンのサイズを基準として、 それを2倍とか3倍にする必要がある。ざっとチェックした限りでは、そのようなコマンド例はなかった。

SVGファイルをパースして、通常は viewBox から設計サイズを把握して、 width、height を2、3倍に変える必要がある。

viewBox がない場合には、元の width、height から viewBox を作ってから、 width、height の値を変更する。

Inkscape 1.0.2

現在のパソコンには Inkscape 1.0.2 がインストールされていた。以前、これを使っていたのであろうか。 ImageMagickもインストールしているため、どちらを使っていたか確かではない。

inkscape --help または inkscape -? でコマンド一覧が表示される。

次のようにすれば、幅(-w) 6画素の png画像ファイルが出力される。出力ディレクトリは予め生成しておく必要がある。

:\map>inkscape -w 6 -p c:/map/img/pattern/circle4.svg -o c:/map/img1.5/circle4.png

デフォルトのサイズは -W オプションで得られる。

c:\map>inkscape -W -p c:/map/img/pattern/circle4.svg
4

svgファイルを読み込んで width="4" から得ることもできる。ただし、 width がないファイルもある。

地図アプリとしては、タイルサイズ256x256画素用の svgファイルを使って、 1.5倍(タブレット用)とか3倍(スマホ用)の pngファイルを作成したい。

256x256画素用の png ファイルは作成済みである。 下記のようにして、 この img ディレクトリに置かれている png ファイル(パス名srcPng)から サイズを得ることができる。

  BufferedImage image = ImageIO.read(new File(srcPng));
  System.out.println(++cnt + ": " + srcPng + " w=" + 
          image.getWidth() + " h=" + image.getHeight());

地図アプリとしては基準のsvgファイルと pngファイルを imgディレクトリに置き、1.5倍(タブレット用)、 3倍(スマホ用)の pngファイルを img1.5、img3 ディレクトリに置く。

取りあえず、書下ろしの下のプログラムでタブレット用とスマホ用のアイコンpngファイルを作成した[2024.4.14]。

/*
javac -d ./class osmutil/*.java
java -classpath ./class OSMUtil -svg2png 1.5
*/

import java.io.*;
import java.nio.*;
import java.util.*;
import java.nio.file.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

public class Svg2Png {

    int cnt;

    void convertAll(String strScale) {
        if (!"1.5".equals(strScale) && !"3".equals(strScale)) {
            System.out.println("Error: [" + strScale + "]");
            return;
        }
        cnt = 0;
        File[] files1 = new File("c:/map/img").listFiles();
        for (File file1 : files1) {
            if (file1.isDirectory()) {
                File[] files2 = file1.listFiles();
                for (File file : files2) {
                    convert(file, strScale);
                }
            } else {
                convert(file1, strScale);
            }
        }
    }

    void convert(File file, String strScale) {
        float scale = Float.parseFloat(strScale);
        String pathSvg = file.getPath();
        if (pathSvg.endsWith(".svg")) {
            String srcPng = pathSvg.replace(".svg", ".png");
            String dstPng = srcPng.replace("\\img\\", "\\img" + strScale + "\\");
            File fileDst = new File(dstPng);
            if (Files.exists(Paths.get(srcPng))) {
                try {
                    if (Files.exists(Paths.get(dstPng))) {
                        System.out.println("exists: " + dstPng);
                        return;
                    }
                    BufferedImage image = ImageIO.read(new File(srcPng));
                    //System.out.println(++cnt + ": " + srcPng + " w=" + 
                    //  image.getWidth() + " h=" + image.getHeight());
                    String dirDst = fileDst.getParent();
                    if (!Files.exists(Paths.get(dirDst))) {
                        Files.createDirectories(Paths.get(dirDst));
                    }
                    String cmd = String.format("inkscape -w %d -p %s -o %s",
                                    (int)(image.getWidth()*scale + 0.5),        // width
                                    pathSvg, fileDst);
                    Runtime rt = Runtime.getRuntime();
                    rt.exec(cmd);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                //System.out.println("no file: " + srcPng);
            }

        }
    }
}

リファレンス

[1] CairoSVG: Convert your SVG files to PDF and PNG.
[2] SVGをPDFとPNGへ変換する方法【Python】
[3] SVG形式からPNG形式への画像の変換方法
[4] How to convert a SVG to a PNG with ImageMagick?