Skip to content

Instantly share code, notes, and snippets.

@marius-m
Created March 4, 2022 12:38
Show Gist options
  • Save marius-m/f4ae68094e808f77072c4e087a79f1be to your computer and use it in GitHub Desktop.
Save marius-m/f4ae68094e808f77072c4e087a79f1be to your computer and use it in GitHub Desktop.
Text area drawing mechanism + font scaling into bounding box
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