Creating png image from SVG command
import java.io.File | |
import java.awt.Color | |
import java.awt.Graphics2D | |
import java.awt.BasicStroke | |
import java.awt.image.BufferedImage | |
import java.awt.geom.AffineTransform | |
import java.awt.geom.GeneralPath as Path | |
import javax.imageio.ImageIO | |
import java.util.ArrayList | |
class SVGParser { | |
private val isUpperCase: (s:String)->Boolean = { it==it.toUpperCase() } | |
private fun createTokenList(svgCmds: String):ArrayList<Token> { | |
val tokenList = ArrayList<Token>() | |
val regex = Regex("[MmLlHhVvZz]") | |
svgCmds.forEach { | |
if( regex.matches( it.toString() ) ){ | |
val token = Token(it) | |
tokenList.add(token) | |
} | |
else { | |
val lastToken = tokenList[tokenList.size-1] | |
lastToken.params += it.toString() | |
} | |
} | |
return tokenList | |
} | |
fun parse(svgCmds: String): Path { | |
val path = Path() | |
var currentX = 0f | |
var currentY = 0f | |
val proc:(token: Token, drawCmd:( x:Float, y:Float)->Unit )->Unit = { token, drawCmd -> | |
// x,y 値を得る | |
val x = token.getX(currentX) | |
val y = token.getY(currentY) | |
// 描写実行 | |
drawCmd(x, y) | |
// currentX,Y 値の更新 | |
currentX = x | |
currentY = y | |
} | |
createTokenList( svgCmds ).forEach { token-> | |
val svgCmd = token.type | |
when(svgCmd) { | |
'M', 'm' -> proc(token, {x,y->path.moveTo(x, y)} ) | |
'L', 'H', 'V', 'l', 'h', 'v' -> proc(token, {x,y->path.lineTo(x, y)} ) | |
'Z', 'z' -> path.closePath() | |
} | |
} | |
return path | |
} | |
class Token(type: Char) { | |
val type = type | |
var params = "" | |
companion object { val BR = System.getProperty("line.separator") } | |
// 改行を削除 | |
private fun fix(s: String): String = s.split( delimiters = BR ).map({it.trim()}).joinToString( separator = "" ) | |
private fun toFloat( params: String, index: Int ): Float { | |
val fixedParams = fix(params) | |
val array = fixedParams.split( regex=Regex("[, ]") ) | |
return if( index<array.size ){ array[index].toFloat() } else { 0f } | |
} | |
fun getX(currentX: Float):Float { | |
var retVal:Float = 0f | |
val v0 = toFloat(params,0) | |
when(type) { | |
'H' -> retVal = v0 // X座標だけがかわるタイプ | |
'h' -> retVal = v0 + currentX // X座標だけがかわるタイプ | |
'V','v' -> retVal = currentX // Y座標だけがかわるタイプ( Xはカレントを維持 ) | |
'M','L' -> retVal = v0 | |
'm','l' -> retVal = v0 + currentX | |
} | |
return retVal | |
} | |
fun getY(currentY: Float):Float { | |
var retVal:Float = 0f | |
val v0 = toFloat(params,0) | |
val v1 = toFloat(params,1) | |
when(type) { | |
'H','h' -> retVal = currentY // X座標だけがかわるタイプ ( Yはカレントを維持 ) | |
'V' -> retVal = v0 // Y座標だけがかわるタイプ | |
'v' -> retVal = v0 + currentY // Y座標だけがかわるタイプ | |
'M','L' -> retVal = v1 | |
'm','l' -> retVal = v1 + currentY | |
} | |
return retVal | |
} | |
} | |
} | |
// | |
// SVGの大きさは 24x24 を基準とする. | |
// | |
val svgCmds = if( args.size>0 ){ args[0] } else { "M16 6 l2.29 2.29 l-4.88 4.88 l-4 -4 L2 16.59 L3.41 18 l6 -6 l4 4 l6.3 -6.29 L22 12 V6 z" } | |
val outputPngFile = if( args.size>1 ){ File(args[1]) } else { File("r.png") } | |
val width = if(args.size>2){ args[2].toInt() } else { 96 } | |
val height = if(args.size>3){ args[3].toInt() } else { 96 } | |
val path = SVGParser().parse(svgCmds) | |
val img = BufferedImage( width, height, BufferedImage.TYPE_4BYTE_ABGR ) | |
val g = img.graphics as Graphics2D | |
g.color = Color.WHITE | |
g.fillRect(0,0,width,height ) | |
val scaleX = width.toDouble()/24.toDouble() | |
val scaleY = height.toDouble()/24.toDouble() | |
val transform = AffineTransform.getScaleInstance( scaleX, scaleY ) | |
val path2 = path.createTransformedShape( transform ) | |
g.color = Color.BLACK | |
g.fill(path2) | |
g.dispose() | |
ImageIO.write(img,"PNG", outputPngFile) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment