Compose Web 한글 깨짐 해결 feat. 폰트 불러오기
HTML로 Wrapping 된 Compose Web 방식이 아닌 SKIKO 자체로 렌더링을 하는 경우, 한글 출력 시 바로 깨져버립니다.
당연히 폰트와 연관있을 것이라 생각하였지만, 아무리 찾아도 폰트를 불러올 수 있는 방법이 없었습니다.
올해 초 실패한 이후, 이제는 인터넷에 관련 내용이 있겠지 생각했는데 놀랍게도 없었습니다.
우선 Compose Multiplatform에서는 Android의 Jetpack Compose처럼 Font를 받는 것이 아닌 ByteArray를 받습니다.
fun Font(
identity: String,
data: ByteArray,
weight: FontWeight = FontWeight.Normal,
style: FontStyle = FontStyle.Normal
): Font = LoadedFont(identity, data, weight, style)
문제는 Kotlin/JS에서는 File 관련 함수가 없다는 것...
Kotlin Script로 폰트 파일을 read 하고 Files로 ByteArray를 write 한 다음 해당 값을 변수에 담는 미련한 방법도 생각해 보았지만 버티질 못하고 당연히 실패...
val file = File(fontPath)
val isExists = file.exists()
val byteArray = file.readBytes()
Files.write(Paths.get("./test.txt"), byteArray.contentToString().toByteArray())
println("isExists: $isExists")
println("byteArray: ${byteArray.contentToString().length}")
이리저리 고민하다 Web에서라도 받아와 사용해보자 생각하여 XMLHttpRequest을 통해 받으려 했지만 responseType을 무엇으로 해야 할지 몰라서 실패...
suspend fun loadFont(name: String, path: String, weight: FontWeight) = suspendCoroutine {
XMLHttpRequest().run {
open("GET", path, false)
responseType = XMLHttpRequestResponseType.ARRAYBUFFER // 무엇으로 해야하냐...
send()
val arrayBuffer = response as? org.khronos.webgl.ArrayBuffer
if (arrayBuffer != null) {
val byteArray = Int8Array(arrayBuffer).unsafeCast<ByteArray>()
val font = Font(identity = name, data = byteArray, weight = weight)
it.resume(font)
} else {
it.resumeWithException(RuntimeException("Failed to load font"))
}
}
}
혹시나하는 생각으로 Ktor를 통해 ByteArray로 받아 처리해보니 정상적으로 돌아갑니다.
suspend fun loadFont(name: String, path: String, weight: FontWeight): Font {
val byteArray = client.get(path).body<ByteArray>()
return Font(identity = name, data = byteArray, weight = weight)
}
일종의 트릭이라 볼 수도 있겠지만, 반년동안 고생하다 이렇게라도 차선책을 놓아봅니다... 여러분들은 저처럼 고생하지 마세요 흑흑...
KMP로 iOS를 위한 KVO에 이어 구글링해도 안나오는 지식을 하나 더 기부하고 갑니다... 이만.. 총총...
역시 아직 코틀린/컴포즈 멀티플랫폼은 미개척지이자 사막에서 바늘 찾기를 하는 시기인 것 같습니다.