commonMain.com.jakewharton.crossword.text.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of crossword-jvm Show documentation
Show all versions of crossword-jvm Show documentation
A 2D canvas for rendering text
The newest version!
package com.jakewharton.crossword
import kotlin.DeprecationLevel.ERROR
private val ansiColorEscape = Regex("""\u001B\[\d+(;\d+)*m""")
fun CharSequence.visualIndex(index: Int): Int {
var remaining = index
forEachVisualCharacter {
if (remaining == 0) {
return it
}
remaining--
}
if (remaining == 0) {
return length
}
throw IndexOutOfBoundsException()
}
val CharSequence.visualWidth: Int get() {
var count = 0
forEachVisualCharacter {
count++
}
return count
}
private inline fun CharSequence.forEachVisualCharacter(block: (index: Int) -> Unit) {
var index = 0
// These values will force a code path that searches for the first real match below.
var nextMatchStart = 0
var nextMatchEnd = -1
val length = length
while (index < length) {
if (index == nextMatchStart) {
// Jump over ANSI control sequence.
index = nextMatchEnd + 1
// Find the next ANSI control sequence, if any.
val match = ansiColorEscape.find(this, index)
if (match != null) {
nextMatchStart = match.range.first
nextMatchEnd = match.range.last
} else {
// No future matches. Ensure we never take this conditional again.
nextMatchStart = length
}
// Restart loop since there may be successive ANSI sequences.
continue
}
block(index)
val code = this[index].code
index++
// Check for a surrogate pair which render as a single visual glyph.
if (code.isHighSurrogate() && index < length && this[index].code.isLowSurrogate()) {
index++
}
// Consume combining diacritics which render on the preceding code point.
while (index < length && this[index].code.isCombiningDiacritical()) {
index++
}
}
}
@Suppress("NOTHING_TO_INLINE")
private inline fun Int.isLowSurrogate(): Boolean = this in 0xDC00..0xDFFF
@Suppress("NOTHING_TO_INLINE")
private inline fun Int.isHighSurrogate(): Boolean = this in 0xD800..0xDBFF
@Suppress("NOTHING_TO_INLINE")
private inline fun Int.isCombiningDiacritical(): Boolean = this in 0x0300..0x036F
© 2015 - 2024 Weber Informatics LLC | Privacy Policy