Skip to content

Commit

Permalink
Improve font size detection
Browse files Browse the repository at this point in the history
  • Loading branch information
gregorbg committed Apr 9, 2021
1 parent 33ba92e commit 221a1cd
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.itextpdf.text.pdf.PdfContentByte
import com.itextpdf.text.pdf.PdfPCell
import com.itextpdf.text.pdf.PdfPTable
import com.itextpdf.text.pdf.PdfWriter
import org.worldcubeassociation.tnoodle.server.model.EventData
import org.worldcubeassociation.tnoodle.svglite.Dimension
import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.util.FontUtil
import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.util.PdfDrawUtil.renderSvgToPDF
Expand Down Expand Up @@ -37,7 +38,7 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode)

val scrambleImageSize = scramblingPuzzle.getPreferredSize(maxScrambleImageWidth, maxScrambleImageHeight)

val allScrambleStrings = scrambleSet.allScrambles.toPDFStrings(scramblingPuzzle.shortName)
val allScrambleStrings = scrambleSet.allScrambles.toPDFStrings(activityCode.eventModel)

val scrambleImageHeight = scrambleImageSize.height.toFloat()
val scrambleColumnWidth = availableWidth - indexColumnWidth - scrambleImageSize.width
Expand All @@ -60,7 +61,11 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode)
isLockedWidth = true
}

val extraScramblesHeader = PdfPCell(Paragraph(TABLE_HEADING_EXTRA_SCRAMBLES)).apply {
val extraParagraph = Paragraph(TABLE_HEADING_EXTRA_SCRAMBLES).apply {
font.style = Font.BOLD
}

val extraScramblesHeader = PdfPCell(extraParagraph).apply {
verticalAlignment = PdfPCell.ALIGN_MIDDLE
horizontalAlignment = PdfPCell.ALIGN_CENTER
fixedHeight = extraScrambleLabelHeight
Expand All @@ -78,20 +83,39 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode)
private fun getFontConfiguration(availableArea: Rectangle, scrambles: List<String>): Font {
val paddedScrambles = scrambles.map { StringUtil.padTurnsUniformly(it, Typography.nbsp.toString()) }

val longestScramble = paddedScrambles.flatMap { it.split(NEW_LINE) }.maxByOrNull { it.length }.orEmpty()
val maxLines = paddedScrambles.map { it.split(NEW_LINE) }.maxOfOrNull { it.count() } ?: 1
val longestScrambleLine = paddedScrambles.flatMap { it.split(NEW_LINE) }.maxByOrNull { it.length }.orEmpty()
val maxLinesCount = paddedScrambles.map { it.split(NEW_LINE) }.maxOfOrNull { it.count() } ?: 1

val fontSize = PdfUtil.fitText(Font(FontUtil.MONO_FONT), longestScramble, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, true)
val fontSizeIfIncludingNewlines = availableArea.height / maxLines
val scramblesHaveLineBreaks = scrambles.any { NEW_LINE in it }

val fontSize = fitFontSize(availableArea, longestScrambleLine, scramblesHaveLineBreaks)
val maxFontSizePerLine = availableArea.height / maxLinesCount

// fontSize should fit horizontally. fontSizeIfIncludingNewlines should fit considering \n
// In case maxLines = 1, fontSizeIfIncludingNewlines is just ignored (as 1 font size should fill the whole rectangle's height)
// in case we have maxLines > 1, we fit width or height and take the min of it.
val perfectFontSize = min(fontSize, fontSizeIfIncludingNewlines)
val perfectFontSize = min(fontSize, maxFontSizePerLine)

return Font(FontUtil.MONO_FONT, perfectFontSize, Font.NORMAL)
}

private fun fitFontSize(availableArea: Rectangle, longestScrambleLine: String, scramblesHaveLineBreaks: Boolean = false): Float {
val fittingFont = Font(FontUtil.MONO_FONT)

return if (scramblesHaveLineBreaks) {
// if our scrambles dictate line breaks, then just calculate the fitting text size right away
PdfUtil.fitText(fittingFont, longestScrambleLine, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, true)
} else {
// try to fit them on one line
val oneLineFontSize = PdfUtil.fitText(fittingFont, longestScrambleLine, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, false)

// is the optimal font size too small to read?
if (oneLineFontSize < FontUtil.MINIMUM_ONE_LINE_FONT_SIZE) {
PdfUtil.fitText(fittingFont, longestScrambleLine, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, true)
} else oneLineFontSize
}
}

private fun requiresHighlighting(scrambleColumnWidth: Float, scrambleFont: Font, scrambles: List<String>): Boolean {
val lineChunks = scrambles.map { it.splitToLineChunks(scrambleFont, scrambleColumnWidth) }

Expand All @@ -105,7 +129,7 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode)
isLockedWidth = true
}

val strScrambles = scrambles.toPDFStrings(scramblingPuzzle.shortName)
val strScrambles = scrambles.toPDFStrings(activityCode.eventModel)

for ((i, scramble) in strScrambles.withIndex()) {
val indexCell = PdfPCell(Paragraph("$scrambleNumberPrefix${i + 1}")).apply {
Expand All @@ -129,6 +153,9 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode)
// let itextpdf wrap lines for us.
isNoWrap = true
verticalAlignment = PdfPCell.ALIGN_MIDDLE
// iText5 defines strange default margins, so we push the text up artificially
paddingTop = SCRAMBLE_CELL_TOP_MARGIN.toFloat()
setLeading(0f, SCRAMBLE_CELL_MULTIPLIED_LEADING.toFloat())
}

table.addCell(scrambleCell)
Expand All @@ -153,16 +180,18 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode)
}

companion object {
private fun List<Scramble>.toPDFStrings(puzzleName: String) =
private fun List<Scramble>.toPDFStrings(event: EventData?) =
flatMap { it.allScrambleStrings }
.takeUnless { puzzleName == "minx" } // minx scrambles intentionally include "\n" chars for alignment
.takeUnless { event == EventData.MEGA } // Megaminx scrambles intentionally include "\n" chars for alignment
?: map { it.scrambleString }

const val MAX_SCRAMBLES_PER_PAGE = 7
const val MAX_SCRAMBLE_IMAGE_RATIO = 3
const val SCRAMBLE_IMAGE_PADDING = 2

const val SCRAMBLE_MARGIN = 5
const val SCRAMBLE_CELL_TOP_MARGIN = -2
const val SCRAMBLE_CELL_MULTIPLIED_LEADING = 1.07

const val EMPTY_CELL_CONTENT = ""

Expand All @@ -176,10 +205,10 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode)
private val HIGHLIGHT_COLOR = BaseColor(230, 230, 230)

const val INDEX_COLUMN_WIDTH_RATIO = 25
const val EXTRA_SCRAMBLES_HEIGHT_RATIO = 30
const val EXTRA_SCRAMBLES_HEIGHT_RATIO = 35

const val VERTICAL_MARGIN = 15f
const val HORIZONTAL_MARGIN = 35f
const val VERTICAL_MARGIN = 15
const val HORIZONTAL_MARGIN = 35

const val NEW_LINE = "\n"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ object PdfUtil {

// Walk past all whitespace that comes immediately after
// the last line wrap we just inserted.
if (first() == ' ') {
if (first().isWhitespace()) {
return drop(1)
.splitLineToChunks(font, availableTextWidth, acc)
}
Expand Down

0 comments on commit 221a1cd

Please sign in to comment.