All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.ru.casperix.multiplatform.text.impl.TextLayoutBuilder.kt Maven / Gradle / Ivy

There is a newer version: 1.1.1
Show newest version
package ru.casperix.multiplatform.text.impl

import ru.casperix.multiplatform.font.localize.CharacterSets
import ru.casperix.multiplatform.font.pixel.PixelFont
import ru.casperix.multiplatform.font.pixel.PixelFontSymbol
import ru.casperix.math.axis_aligned.float32.Box2f
import ru.casperix.math.vector.float32.Vector2f
import ru.casperix.multiplatform.text.SymbolLayout
import ru.casperix.multiplatform.text.TextLayout
import ru.casperix.multiplatform.text.TextRenderConfig

@ExperimentalUnsignedTypes
object TextLayoutBuilder {
    fun layout(text: String, areaSize: Vector2f, font: PixelFont): TextLayout {
        val blocks = TextProcessor.splitByGroups(text)
        val blockLayouts = blocks.map { word ->
            layoutBlock(font, word, font.symbolMap, TextRenderConfig.textMissingSymbolBehaviour)
        }

        val symbols = placeSymbols(font, blockLayouts, areaSize)
        return TextLayout(font.metrics, text, symbols)
    }

    private fun placeSymbols(font: PixelFont, layouts: List, areaSize: Vector2f): List {
        var baselineOffset = Vector2f(0f, font.metrics.ascent)
        val lineHeight = font.lineHeight

        val layoutOffsets = layouts.map { block ->
            val requestArea = block.getArea()

            val isCarriageReturn = block.text == "\n"

            val lastPosition = baselineOffset
            if (isCarriageReturn || (baselineOffset.x > 0f && baselineOffset.x + requestArea.dimension.x > areaSize.x)) {
                baselineOffset = Vector2f(0f, baselineOffset.y + lineHeight)
            }
            val blockPosition = if (isCarriageReturn) lastPosition else baselineOffset
            baselineOffset += requestArea.dimension.xAxis
            Pair(blockPosition, block)
        }

        val symbols = layoutOffsets.flatMap { (offset, layout) ->
            layout.symbols.map {
                it.copy(pivot = it.pivot + offset)
            }
        }
        return symbols
    }


    private fun layoutBlock(
        font: PixelFont,
        text: String,
        fontSymbols: Map,
        missingSymbolBehaviour: MissingSymbolBehaviour
    ): TextLayout {
        var offset = Vector2f.ZERO
        val metrics = font.metrics

        val symbols = text.flatMap { char ->
            generateGlyphList(char, fontSymbols, missingSymbolBehaviour)
        }.map { symbol ->
            val symbolWidth = symbol.size.x.toFloat()
            val position = offset
            offset += Vector2f(symbolWidth, 0f)

            val symbolArea = Box2f(
                -Vector2f(0f, metrics.ascent),
                Vector2f(0f, metrics.descent) + symbol.size.xAxis.toVector2f()
            )
            SymbolLayout(position, symbol, symbolArea)
        }

        return TextLayout(font.metrics, text, symbols)
    }

    private fun generateGlyphList(
        char: Char,
        symbolMap: Map,
        missingSymbolBehaviour: MissingSymbolBehaviour
    ): List {
        val symbol = symbolMap[char]
        return if (symbol != null) {
            listOf(symbol)
        } else if (char == '\n') {
            emptyList()
        } else {
            when (missingSymbolBehaviour) {
                MissingSymbolBehaviour.IGNORE -> emptyList()
                MissingSymbolBehaviour.SHOW_CHAR_CODE -> ("#" + char.code.toString() + ";").mapNotNull {
                    symbolMap[it]
                }

                MissingSymbolBehaviour.SHOW_WHITE_SQUARE -> listOfNotNull(symbolMap[CharacterSets.WHITE_QUAD])
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy