Friday, March 18, 2016 / kotlin, DTP, java2d

kotlin でPNGファイルを一括でJPGファイルに変換するスクリプトを書いた件

kotlin のコレクションが素敵。DTP関連のタスクでは入稿してきた大量のデータの前処理してこちらの都合のよいファイル名や形式に直す必要が生じることがある。普段は groovy で処理しているのだが、kotlin のコレクションが素敵そうなので、試してみました。

環境は Mac OS X El Capitan, Terminal / javavm は java7 / kotlin は 1.0.0 を使用しています。 kotlinc などのコマンドが使える状態になっている前提で話を進めます。

ここでは png としてデータ入稿されたが jpg として処理しなければいけなかった... という仮の話を題材にします。 ファイル名リストから (pngファイル名,jpgファイル名) のペアリストを生成してから、png 形式の画像を jpg 形式に直して保存するところまでやってみます。

Step 0) 入稿してきたデータ

以下のような png ファイルがきたことにします。

$ ls *.png
001.png 002.png 003.png 004.png 005.png

Step 1) png ファイルの拡張子を取り除いたファイル名のリストを生成

step1.kts

import java.io.File

// step1
val basefilenameList = File(".").listFiles().filter{it.extension=="png"}.map{ it.nameWithoutExtension }
println( basefilenameList )

結果

$ kotlinc -script step1.kts
[001, 002, 003, 004, 005]

Step 2) pngファイル名リスト、jpgファイル名リストをそれぞれ生成

// step2
val pngfilenameList = basefilenameList.map { "$it.png" }
val jpgfilenameList = basefilenameList.map { "$it.jpg" }
println( pngfilenameList )
println( jpgfilenameList )

生成結果

[001.png, 002.png, 003.png, 004.png, 005.png]
[001.jpg, 002.jpg, 003.jpg, 004.jpg, 005.jpg]

Step 3) zipで(pngファイル名、jpgファイル名)のペアリストを生成

// step3
val zippedList = pngfilenameList.zip( jpgfilenameList )
println( zippedList )

生成結果

[(001.png, 001.jpg), (002.png, 002.jpg), (003.png, 003.jpg), (004.png, 004.jpg), (005.png, 005.jpg)]

ここまでの成果のまとめ

test1.kts

import java.io.File

val basefilenameList = File(".").listFiles().filter{it.extension=="png"}.map{ it.nameWithoutExtension }
val pngfilenameList = basefilenameList.map { "$it.png" }
val jpgfilenameList = basefilenameList.map { "$it.jpg" }
val zippedList = pngfilenameList.zip( jpgfilenameList )
println( zippedList )

実行と結果

$ kotlinc -script test1.kts
[(001.png, 001.jpg), (002.png, 002.jpg), (003.png, 003.jpg), (004.png, 004.jpg), (005.png, 005.jpg)]

Step 4) png を jpg に変換する function を追加

fun png2jpg( pngFile:File, jpgFile:File ){
    val pngImg = ImageIO.read(pngFile)
    val bImg = BufferedImage(pngImg.width,pngImg.height,BufferedImage.TYPE_3BYTE_BGR )
    val g:Graphics2D = bImg.graphics as Graphics2D
    g.drawImage(pngImg,0,0,null)
    g.dispose()
    ImageIO.write(bImg,"JPEG",jpgFile)
}

Step 5) すべてをまとめる

png2jpg.kts

import java.io.File
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import javax.imageio.ImageIO

fun png2jpg( pngFile:File, jpgFile:File ){
    val pngImg = ImageIO.read(pngFile)
    val bImg = BufferedImage(pngImg.width,pngImg.height,BufferedImage.TYPE_3BYTE_BGR )
    val g:Graphics2D = bImg.graphics as Graphics2D
    g.drawImage(pngImg,0,0,null)
    g.dispose()
    ImageIO.write(bImg,"JPEG",jpgFile)
}

val basefilenameList = File(".").listFiles().filter{it.extension=="png"}.map{ it.nameWithoutExtension }
val pngfilenameList = basefilenameList.map { "$it.png" }
val jpgfilenameList = basefilenameList.map { "$it.jpg" }
pngfilenameList.zip( jpgfilenameList ).forEach { png2jpg( File(it.first), File(it.second) ) }

関数名が直感的なのがやはり素敵です。 以上です。

おまけ

groovy でも実装してみました。( groovy 2.4 で作動テスト ) groovy では zip 相当の機能は transpose です。

png2jpg.groovy

import java.io.File
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import javax.imageio.ImageIO

def png2jpg = { File pngFile, File jpgFile->
    def pngImg = ImageIO.read(pngFile)
    def bImg = new BufferedImage(pngImg.width,pngImg.height,BufferedImage.TYPE_3BYTE_BGR )
    def g = bImg.graphics
    g.drawImage(pngImg,0,0,null)
    g.dispose()
    ImageIO.write(bImg,"JPEG",jpgFile)
}

def basefilenameList = new File(".").listFiles().findAll{it.name.endsWith("png")}.collect{it.name.replace(".png","")}
def pngfilenameList = basefilenameList.collect { "${it}.png" }
def jpgfilenameList = basefilenameList.collect { "${it}.jpg" }

[pngfilenameList, jpgfilenameList].transpose().each { pngfilename, jpgfilename->
    png2jpg( new File(pngfilename), new File(jpgfilename) )
}