Prevent client from rendering text on server if text is blank. Trim text in server...
authorLanius Trolling <lanius@laniustrolling.dev>
Thu, 7 Mar 2024 13:20:38 +0000 (08:20 -0500)
committerLanius Trolling <lanius@laniustrolling.dev>
Thu, 7 Mar 2024 13:20:38 +0000 (08:20 -0500)
src/jvmMain/kotlin/info/mechyrdia/Factbooks.kt
src/jvmMain/kotlin/info/mechyrdia/lore/fonts.kt
src/jvmMain/resources/static/init.js

index 92aeff344f295ba264d3b10600976b5b38b492c8..9a8d3793a13925254d12b4122dac03119a6e3207 100644 (file)
@@ -275,7 +275,7 @@ fun Application.factbooks() {
                        
                        val text = call.receiveText()
                        val svg = runInterruptible(Dispatchers.Default) {
-                               MechyrdiaSansFont.renderTextToSvg(text, isBold, isItalic, align)
+                               MechyrdiaSansFont.renderTextToSvg(text.trim(), isBold, isItalic, align)
                        }
                        
                        call.respondText(svg, ContentType.Image.SVG)
index 5924dcd00970a88938c7b17d8b5e4e8e458de496..f61f91c9aa3c38a5fcea36f1d23e9b54be85c013 100644 (file)
@@ -22,13 +22,29 @@ import kotlin.properties.ReadOnlyProperty
 import kotlin.reflect.KProperty
 
 object MechyrdiaSansFont {
-       enum class Alignment(val amount: Double) {
-               LEFT(0.0), CENTER(0.5), RIGHT(1.0),
+       enum class Alignment {
+               LEFT {
+                       override fun processWidth(widthDiff: Int): Int {
+                               return 0
+                       }
+               },
+               CENTER {
+                       override fun processWidth(widthDiff: Int): Int {
+                               return widthDiff / 2
+                       }
+               },
+               RIGHT {
+                       override fun processWidth(widthDiff: Int): Int {
+                               return widthDiff
+                       }
+               };
+               
+               abstract fun processWidth(widthDiff: Int): Int
        }
        
        fun renderTextToSvg(text: String, bold: Boolean, italic: Boolean, align: Alignment): String {
                val (file, font) = getFont(bold, italic)
-               return layoutText(text, file, font, align.amount).toSvgDocument(96.0 / file.unitsPerEm)
+               return layoutText(text, file, font, align).toSvgDocument(80.0 / file.unitsPerEm, 12.0)
        }
        
        private val fontsRoot = File(Configuration.CurrentConfiguration.rootDir, "fonts")
@@ -151,7 +167,7 @@ object MechyrdiaSansFont {
                return widths.zip(glyphPositions) { width, pos -> width + pos[2] }.sum()
        }
        
-       private fun layoutText(text: String, file: TTFFile, font: Font, alignAmount: Double): Shape {
+       private fun layoutText(text: String, file: TTFFile, font: Font, align: Alignment): Shape {
                val img = BufferedImage(256, 160, BufferedImage.TYPE_INT_ARGB)
                val g2d = img.createGraphics()
                try {
@@ -164,7 +180,7 @@ object MechyrdiaSansFont {
                        val lineAdjust = lineGlyphs.zip(lineBasics) { glyphs, widths -> file.getGlyphPositions(glyphs, widths) }
                        val lineWidths = lineBasics.zip(lineAdjust) { width, adjust -> getWidth(width, adjust) }
                        val blockWidth = lineWidths.max()
-                       var ly = 0.0
+                       var ly = 0
                        
                        yieldThread()
                        
@@ -173,7 +189,7 @@ object MechyrdiaSansFont {
                        for ((li, line) in lines.withIndex()) {
                                if (line.isNotBlank()) {
                                        val lineWidth = lineWidths[li]
-                                       val lx = (blockWidth - lineWidth) * alignAmount
+                                       val lx = align.processWidth(blockWidth - lineWidth)
                                        
                                        var cx = 0
                                        var cy = 0
@@ -187,8 +203,7 @@ object MechyrdiaSansFont {
                                                val glyphShape = glyph.outline as GeneralPath
                                                val glyphShift = adjusted[ci]
                                                
-                                               tf.setToIdentity()
-                                               tf.translate(lx + cx + glyphShift[0], ly + cy + glyphShift[1])
+                                               tf.setToTranslation((lx + cx + glyphShift[0]).toDouble(), (ly + cy + glyphShift[1]).toDouble())
                                                shape.append(glyphShape.getPathIterator(tf), false)
                                                
                                                cx += glyphShift[2] + basicAdv[ci]
@@ -210,12 +225,15 @@ object MechyrdiaSansFont {
                }
        }
        
-       private fun Shape.toSvgDocument(scale: Double): String {
+       private fun Shape.toSvgDocument(scale: Double, padding: Double = 0.0): String {
                return buildString {
                        appendLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
                        
                        val viewBox = bounds2D
-                       appendLine("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"${viewBox.width * scale}\" height=\"${viewBox.height * scale}\" viewBox=\"${viewBox.minX} ${viewBox.minY} ${viewBox.width} ${viewBox.height}\">")
+                       val vBoxPad = padding / scale
+                       val sizePad = padding * 2
+                       
+                       appendLine("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${(viewBox.width * scale) + sizePad}\" height=\"${(viewBox.height * scale) + sizePad}\" viewBox=\"${viewBox.minX - vBoxPad} ${viewBox.minY - vBoxPad} ${viewBox.width + (vBoxPad * 2)} ${viewBox.height + (vBoxPad * 2)}\">")
                        appendLine(toSvgPath())
                        appendLine("</svg>")
                }
index c1913ef7e39f353d77debe0b9bba10286b460652..388ab355108fa76a0b47e035ed7ade258d7a6743 100644 (file)
                        await delay(delayLength);
                        if (inText !== input.value) return;
 
-                       let queryString = "?";
-                       queryString += boldOpt.checked ? "bold=true&" : "";
-                       queryString += italicOpt.checked ? "italic=true&" : "";
-                       queryString += "align=" + alignOpt.value;
-
-                       const outBlob = await (await fetch('/mechyrdia-sans' + queryString, {
-                               method: 'POST',
-                               headers: {
-                                       'Content-Type': 'text/plain',
-                               },
-                               body: inText,
-                       })).blob();
-
-                       if (inText !== input.value) return;
+                       let outBlob;
+                       if (inText.trim().length === 0) {
+                               outBlob = new Blob([
+                                       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
+                                       "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"0\" height=\"0\">\n",
+                                       "</svg>\n"
+                               ], {type: "image/svg+xml"});
+                       } else {
+                               let queryString = "?";
+                               queryString += boldOpt.checked ? "bold=true&" : "";
+                               queryString += italicOpt.checked ? "italic=true&" : "";
+                               queryString += "align=" + alignOpt.value;
+
+                               outBlob = await (await fetch('/mechyrdia-sans' + queryString, {
+                                       method: 'POST',
+                                       headers: {
+                                               'Content-Type': 'text/plain',
+                                       },
+                                       body: inText,
+                               })).blob();
+
+                               if (inText !== input.value) return;
+                       }
 
                        const prevObjectUrl = output.src;
                        if (prevObjectUrl != null && prevObjectUrl.length > 0)
                                outputBox.value = inputBox.value;
                        });
                }
+       });
 
+       window.addEventListener("load", function () {
                // Kishari alphabet
                const kishariAlphabetBoxes = document.getElementsByClassName("kishari-alphabet-box");
                for (const kishariAlphabetBox of kishariAlphabetBoxes) {