From 5d7eca6185a8b11ba607b696934d6912dba66d6f Mon Sep 17 00:00:00 2001 From: Lanius Trolling Date: Sun, 4 Feb 2024 10:05:12 -0500 Subject: [PATCH] Refactor code and add interruptibility --- .../kotlin/info/mechyrdia/BlockingCode.kt | 7 +++ .../kotlin/info/mechyrdia/lore/fonts.kt | 50 +++++++++++-------- 2 files changed, 35 insertions(+), 22 deletions(-) create mode 100644 src/jvmMain/kotlin/info/mechyrdia/BlockingCode.kt diff --git a/src/jvmMain/kotlin/info/mechyrdia/BlockingCode.kt b/src/jvmMain/kotlin/info/mechyrdia/BlockingCode.kt new file mode 100644 index 0000000..b412225 --- /dev/null +++ b/src/jvmMain/kotlin/info/mechyrdia/BlockingCode.kt @@ -0,0 +1,7 @@ +package info.mechyrdia + +fun yieldThread() { + if (Thread.interrupted()) { + throw InterruptedException() + } +} diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/fonts.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/fonts.kt index 6ba6581..d858f52 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/fonts.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/fonts.kt @@ -1,5 +1,6 @@ package info.mechyrdia.lore +import info.mechyrdia.yieldThread import java.awt.Font import java.awt.RenderingHints import java.awt.Shape @@ -13,20 +14,8 @@ object MechyrdiaSansFont { LEFT(0.0), CENTER(0.5), RIGHT(1.0), } - fun renderTextToSvg(text: String, bold: Boolean, italic: Boolean, align: Alignment, standalone: Boolean = true): String { - val font = if (bold) { - if (italic) - mechyrdiaSansBI - else - mechyrdiaSansB - } else - if (italic) - mechyrdiaSansI - else - mechyrdiaSans - - val shape = layoutText(text, font, align.amount) - return shape.toSvgDocument(standalone) + fun renderTextToSvg(text: String, bold: Boolean, italic: Boolean, align: Alignment): String { + return layoutText(text, getFont(bold, italic), align.amount).toSvgDocument() } private const val DEFAULT_FONT_SIZE = 48f @@ -42,6 +31,11 @@ object MechyrdiaSansFont { private val mechyrdiaSansI = loadFont("mechyrdia-sans-italic") private val mechyrdiaSansBI = loadFont("mechyrdia-sans-bold-italic") + private val mechyrdiaSansFonts = listOf(mechyrdiaSans, mechyrdiaSansI, mechyrdiaSansB, mechyrdiaSansBI) + private fun getFont(bold: Boolean, italic: Boolean): Font { + return mechyrdiaSansFonts[(if (bold) 2 else 0) + (if (italic) 1 else 0)] + } + private fun layoutText(text: String, font: Font, alignAmount: Double): Shape { val img = BufferedImage(256, 160, BufferedImage.TYPE_INT_ARGB) val g2d = img.createGraphics() @@ -54,12 +48,15 @@ object MechyrdiaSansFont { val width = lines.maxOf { fontMetrics.stringWidth(it) }.toDouble() var y = 0.0 + yieldThread() + val shape = GeneralPath() val tf = AffineTransform() for (line in lines) { if (line.isNotBlank()) { val x = (width - fontMetrics.stringWidth(line)) * alignAmount + // Mechyrdia Sans only supports the Latin alphabet, so we can ignore bidirectional text val glyphs = font.layoutGlyphVector(g2d.fontRenderContext, line.toCharArray(), 0, line.length, Font.LAYOUT_LEFT_TO_RIGHT) val textShape = glyphs.outline as GeneralPath @@ -69,6 +66,8 @@ object MechyrdiaSansFont { } y += fontMetrics.height + + yieldThread() } return shape @@ -77,10 +76,9 @@ object MechyrdiaSansFont { } } - private fun Shape.toSvgDocument(standalone: Boolean = true): String { + private fun Shape.toSvgDocument(): String { return buildString { - if (standalone) - appendLine("") + appendLine("") val viewBox = bounds2D appendLine("") @@ -95,32 +93,40 @@ object MechyrdiaSansFont { val iterator = getPathIterator(null) val coords = DoubleArray(6) + var isInitial = true while (!iterator.isDone) { + if (isInitial) + isInitial = false + else + append(' ') + when (val segment = iterator.currentSegment(coords)) { PathIterator.SEG_MOVETO -> { - append("M ${coords[0]},${coords[1]} ") + append("M ${coords[0]},${coords[1]}") } PathIterator.SEG_LINETO -> { - append("L ${coords[0]},${coords[1]} ") + append("L ${coords[0]},${coords[1]}") } PathIterator.SEG_QUADTO -> { - append("Q ${coords[0]},${coords[1]} ${coords[2]},${coords[3]} ") + append("Q ${coords[0]},${coords[1]} ${coords[2]},${coords[3]}") } PathIterator.SEG_CUBICTO -> { - append("C ${coords[0]},${coords[1]} ${coords[2]},${coords[3]} ${coords[4]},${coords[5]} ") + append("C ${coords[0]},${coords[1]} ${coords[2]},${coords[3]} ${coords[4]},${coords[5]}") } PathIterator.SEG_CLOSE -> { - append("Z ") + append("Z") } else -> error("Invalid segment type $segment") } iterator.next() + + yieldThread() } append("\" fill-rule=\"") -- 2.25.1