Created
March 4, 2022 12:38
-
-
Save marius-m/f4ae68094e808f77072c4e087a79f1be to your computer and use it in GitHub Desktop.
Text area drawing mechanism + font scaling into bounding box
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import lt.ito.export.TextAreaLineCounter | |
import sun.font.FontDesignMetrics | |
import java.awt.Font | |
import java.awt.FontMetrics | |
import java.awt.Graphics | |
/** | |
* Scales font into bounding [boxWidth], [boxHeight] to fit in and draws it using word wrap | |
*/ | |
class TextAreaScaleDrawer( | |
private val text: String, | |
private val maxFontSize: Int, | |
private val boxHeight: Double, | |
private val boxWidth: Double | |
) { | |
private var _font: Font = Font(null, Font.LAYOUT_LEFT_TO_RIGHT, maxFontSize) | |
val font: Font | |
get() = _font | |
fun draw(graphics: Graphics) { | |
val scaleWrapResult = scaleFontToFit( | |
maxFontSize = maxFontSize | |
) | |
graphics.font = scaleWrapResult.font | |
val wrapTexts = scaleWrapResult | |
.paragraph | |
.sentences | |
.flatMap { it.wraps } | |
.map { it.wrapText } | |
val nextLineStep = graphics.fontMetrics.height | |
var yNextLine = nextLineStep | |
wrapTexts.forEach { text -> | |
graphics.drawString(text, 0, yNextLine) | |
yNextLine += nextLineStep | |
} | |
} | |
/** | |
* Shrinks font size to fit in this text area | |
*/ | |
private fun scaleFontToFit( | |
maxFontSize: Int | |
): ScaleWrapResult { | |
val maxHeight = boxHeight | |
val lineCounter = TextAreaLineCounter(textProvider) | |
var textSize = maxFontSize | |
var font = newFontWithSize(textSize) | |
var fm = newFontMetrics(font) | |
var paragraph = lineCounter.parseParagraph(font, fm) | |
var textHeight = newTextHeight(fm, paragraph) | |
while (textHeight > maxHeight) { | |
textSize -= 1 | |
font = newFontWithSize(textSize) | |
fm = newFontMetrics(font) | |
paragraph = lineCounter.parseParagraph(font, fm) | |
textHeight = newTextHeight(fm, paragraph) | |
} | |
// For easier text debugging | |
// TextAreaLineCounter.l.debug("LC:lineCount(${paragraph.lineCount()}:${paragraph})/${this.text.inShort()}") | |
return ScaleWrapResult(font, paragraph) | |
} | |
/** | |
* Sometimes 'feet' are cut off when comparing text. | |
* So we're giving a bit more 'room', for the feed to be drawn with extra [DEFAULT_EXTRA_TEXT_HEIGHT_COEFFICIENT] | |
*/ | |
private fun newTextHeight( | |
fm: FontMetrics, | |
paragraph: TextAreaLineCounter.WrappedParagraph | |
): Double { | |
return paragraph.lineCount() * (fm.height * DEFAULT_EXTRA_TEXT_HEIGHT_COEFFICIENT) | |
} | |
private fun newFontWithSize(size: Int): Font { | |
return Font(_font.name, _font.style, size) | |
} | |
private fun newFontMetrics(font: Font): FontMetrics { | |
return FontDesignMetrics.getMetrics(font) | |
} | |
private class ScaleWrapResult( | |
val font: Font, | |
val paragraph: TextAreaLineCounter.WrappedParagraph | |
) | |
private val textProvider = object : TextAreaLineCounter.TextProvider { | |
override val text: String = this@TextAreaScaleDrawer.text | |
override val formatWidth: Float = boxWidth.toFloat() | |
} | |
companion object { | |
const val DEFAULT_EXTRA_TEXT_HEIGHT_COEFFICIENT = 1.02 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment