Skip to content

Instantly share code, notes, and snippets.

@Skhmt
Last active June 8, 2019 08:05
Show Gist options
  • Save Skhmt/b5d1b4145b5d316ca74dee652b669e94 to your computer and use it in GitHub Desktop.
Save Skhmt/b5d1b4145b5d316ca74dee652b669e94 to your computer and use it in GitHub Desktop.
png mixed content
import io.javalin.Javalin
import java.util.zip.CRC32
import java.util.zip.Deflater
/*
main requires:
io.javalin:javalin:2.7.0
org.slf4j:slf4j-simple:1.7.25
test at:
https://skhmt.github.io/https.html
*/
fun main(args: Array<String>) {
val port = 80
val app = Javalin.create()
app.enableCorsForAllOrigins()
app.start(port)
println("running on port $port")
app.get("/get.png") { ctx ->
val msg = ctx.queryParam("msg")
println("${ctx.ip()}: msg=$msg")
val png = makePNG(msg!!)
ctx.result(png.inputStream())
}
}
fun makePNG(data: String): ByteArray {
val dataAsByteArray = data.toByteArray(Charsets.UTF_8) // string (utf8) as a byte array
return (pngSignature() +
pngIHDR(dataAsByteArray.size,1) +
pngIDAT(dataAsByteArray) +
pngIEND())
}
// PNG Signature - https://www.w3.org/TR/PNG/#5PNG-file-signature
fun pngSignature(): ByteArray {
return byteArrayOf(-119,80,78,71,13,10,26,10)
}
// PNG IHDR chunk - https://www.w3.org/TR/PNG/#11IHDR
fun pngIHDR(width: Int, height: Int): ByteArray {
val ihdrLength = byteArrayOf(0,0,0,13)
val ihdrType = byteArrayOf(73,72,68,82)
val ihdrData = byteArrayOf(
*intToBA(width), // width
*intToBA(height), // height
8, // bitdepth - 8 so each pixel is a byte
0, // color type - 0 is greyscale
0,0,0 // compression, filter, and interlace methods - must be 0
)
val ihdrCRC = getCRC(ihdrType, ihdrData)
return (ihdrLength +
ihdrType +
ihdrData +
ihdrCRC)
}
// PNG IDAT chunk - https://www.w3.org/TR/PNG/#11IDAT
fun pngIDAT(data: ByteArray): ByteArray {
val idatType = byteArrayOf(73,68,65,84)
val idatData = deflate(byteArrayOf(0, *data)) // filter type 0 (no filter)
val idatCRC = getCRC(idatType, idatData)
val idatLength = intToBA(idatData.size) // compressed data length
return (idatLength +
idatType +
idatData +
idatCRC)
}
// PNG IEND chunk - https://www.w3.org/TR/PNG/#11IEND
fun pngIEND(): ByteArray {
return byteArrayOf(0,0,0,0,73,69,78,68,-82,66,96,-126)
}
// convenience function to use Java's built-in CRC32 library
private fun getCRC(ba1: ByteArray, ba2: ByteArray): ByteArray {
val ba = ba1 + ba2
val crc = CRC32()
crc.update(ba)
return intToBA(crc.value.toInt())
}
// this is faster than using a for-loop and faster than ByteBuffer by 15-20x
private fun intToBA(num: Int): ByteArray {
val ba = ByteArray(4)
ba[3] = (num and 0xff).toByte()
ba[2] = ((num shr 8) and 0xff).toByte()
ba[1] = ((num shr 16) and 0xff).toByte()
ba[0] = ((num shr 24) and 0xff).toByte()
return ba
}
// convenience function to use Java's built-in Deflater library
// the buffer can be modified if necessary; it should probably be 32767
private fun deflate(ba: ByteArray): ByteArray {
val buffer = ByteArray(1048576) // 2^20 = 1024*1024 = 1MB max
val deflater = Deflater()
deflater.setInput(ba)
deflater.finish()
val length = deflater.deflate(buffer)
return buffer.sliceArray(0 until length)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment