From 5243e352c6b3d7013731242449fadc76d18c8732 Mon Sep 17 00:00:00 2001 From: Gregor Billing Date: Mon, 5 Apr 2021 02:22:49 +0200 Subject: [PATCH 1/3] Restore status quo for GeneralPDF rendering --- .../webscrambles/pdf/GeneralScrambleSheet.kt | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt b/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt index 084022b25..87b6aff70 100644 --- a/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt +++ b/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt @@ -5,12 +5,15 @@ 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 import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.util.PdfUtil import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.util.PdfUtil.splitToLineChunks +import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.util.StringUtil import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.model.* +import java.io.File import kotlin.math.min class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) : BaseScrambleSheet(scrambleSet, activityCode) { @@ -75,16 +78,28 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) } private fun getFontConfiguration(availableArea: Rectangle, scrambles: List): Font { - val longestScramble = scrambles.flatMap { it.split(NEW_LINE) }.maxByOrNull { it.length }.orEmpty() - val maxLines = scrambles.map { it.split(NEW_LINE) }.map { it.count() }.maxOrNull() ?: 1 + val longestScramble = scrambles.maxByOrNull { it.length }.orEmpty() - val fontSize = PdfUtil.fitText(Font(FontUtil.MONO_FONT), longestScramble, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, true) // FIXME const - val fontSizeIfIncludingNewlines = availableArea.height / maxLines + val longestPaddedScramble = scrambles.map { StringUtil.padTurnsUniformly(it, WIDEST_CHAR_STRING) } + .maxByOrNull { it.length } ?: longestScramble - // 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 longestScrambleOneLine = NEW_LINE !in longestPaddedScramble + + // I don't know how to configure ColumnText.fitText's word wrapping characters, + // so instead, I just replace each character I don't want to wrap with M, which + // should be the widest character (we're using a monospaced font, + // so that doesn't really matter), and won't get wrapped. + val longestScrambleMasked = WIDEST_CHAR_STRING.repeat(longestScramble.length) + val longestPaddedScrambleMasked = longestPaddedScramble.replace(" ".toRegex(), WIDEST_CHAR_STRING) + + val fontSizeWithoutPadding = PdfUtil.fitText(Font(FontUtil.MONO_FONT), longestScrambleMasked, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, false) + + // If the scramble contains newlines, then we *only* allow wrapping at the newlines. + val longestRespectingNewlines = longestPaddedScramble.takeIf { longestScrambleOneLine } ?: longestPaddedScrambleMasked + val fontSizeIfIncludingNewlines = PdfUtil.fitText(Font(FontUtil.MONO_FONT), longestRespectingNewlines, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, true) + + val oneLine = longestScrambleOneLine && fontSizeWithoutPadding >= FontUtil.MINIMUM_ONE_LINE_FONT_SIZE + val perfectFontSize = fontSizeWithoutPadding.takeIf { oneLine } ?: fontSizeIfIncludingNewlines return Font(FontUtil.MONO_FONT, perfectFontSize, Font.NORMAL) } @@ -160,6 +175,7 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) const val SCRAMBLE_IMAGE_PADDING = 2 const val SCRAMBLE_MARGIN = 5 + const val WIDEST_CHAR_STRING = Typography.nbsp.toString() const val EMPTY_CELL_CONTENT = "" @@ -179,5 +195,26 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) const val HORIZONTAL_MARGIN = 35f const val NEW_LINE = "\n" + + @JvmStatic + fun main(args: Array) { + val rawScrambles = listOf( + "F' D2 B2 Dw' Fw' U2 Dw Fw Lw' D2 Dw Bw' B' L Lw D' Lw B' F Fw' Rw' Uw D Bw2 Uw D U Rw' L2 B L2 R2 F' D F B2 Dw2 B' Dw Uw' B' Rw2 Lw' Uw' Fw Bw' R2 B' Uw Dw R' Uw' Lw2 R2 B2 Rw B' Bw' Rw2 R2 3Fw 3Uw2", + "Fw2 Uw2 Fw' R U2 R Bw U' Fw2 U' Rw Fw2 Uw' Bw2 B2 F2 Rw2 F' Fw D2 Fw' Uw2 R' Rw F Fw' R L2 Rw2 Uw R2 Dw' F' U2 Uw2 F2 L' U Dw' Rw2 Bw2 Dw R' F' R F' L' U2 Dw R L Rw' D F2 L2 D F B L2 F' 3Uw", + "Lw D2 F' U R' Dw2 L Fw F R2 Uw' Dw L' R' Rw U2 Fw' Dw2 Fw' U Bw2 Uw Bw2 B' Fw' Rw Dw2 F2 Uw Dw B R2 F B' L2 U' R' L Lw2 Uw2 Fw' U Rw Lw2 R' Bw2 Lw B' Rw Uw F D' Uw Dw Bw Lw R Uw U2 3Rw' 3Uw", + "D' F2 Uw2 Fw Rw' Bw L2 Uw L2 Fw2 Dw2 D Uw2 B R' F' Rw2 D' U' Bw2 Dw B' U Rw2 R2 Fw D Dw2 Bw2 Fw' Lw' Fw Dw' F' U Bw Uw2 Bw R Rw U' Bw2 Lw' Rw' Uw Lw' Uw2 Bw Fw' Dw Bw F Lw' Dw U2 Fw2 Dw Fw2 U2 Rw2 3Fw", + "Rw2 L Uw Rw' Fw' Rw' F Fw2 U2 D R U B' U2 B2 F' Lw' F Lw2 L2 Rw2 Fw' D2 Dw' U' Lw' B D' Fw' U Uw' Fw L' F2 R2 D2 B U D2 L B2 D' F2 Dw D Rw D' Lw F' U' Uw2 Fw' Bw' R Lw' F Lw2 Rw Fw2 F 3Rw2 3Uw" + ) + + val scrambles = rawScrambles.map { Scramble(it) } + val scrambleSet = ScrambleSet(42, scrambles, emptyList()) + + val activityCode = ActivityCode.compile(EventData.FIVE_BLD, 0, 0, 0) + + val pdfSheet = GeneralScrambleSheet(scrambleSet, activityCode) + val pdfBytes = pdfSheet.render() + + File(File(System.getProperty("user.home")), "TNoodleDummy.pdf").writeBytes(pdfBytes) + } } } From d07ddf8826550c306bd90986600989de90ce6759 Mon Sep 17 00:00:00 2001 From: Gregor Billing Date: Wed, 7 Apr 2021 00:03:32 +0200 Subject: [PATCH 2/3] Fix scrambles occasionally filling more than one page --- .../webscrambles/pdf/GeneralScrambleSheet.kt | 52 ++++--------------- 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt b/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt index 87b6aff70..2de792362 100644 --- a/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt +++ b/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt @@ -5,7 +5,6 @@ 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 @@ -13,7 +12,6 @@ import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.util.PdfUtil import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.util.PdfUtil.splitToLineChunks import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.util.StringUtil import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.model.* -import java.io.File import kotlin.math.min class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) : BaseScrambleSheet(scrambleSet, activityCode) { @@ -78,28 +76,18 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) } private fun getFontConfiguration(availableArea: Rectangle, scrambles: List): Font { - val longestScramble = scrambles.maxByOrNull { it.length }.orEmpty() + val paddedScrambles = scrambles.map { StringUtil.padTurnsUniformly(it, Typography.nbsp.toString()) } - val longestPaddedScramble = scrambles.map { StringUtil.padTurnsUniformly(it, WIDEST_CHAR_STRING) } - .maxByOrNull { it.length } ?: longestScramble + 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 longestScrambleOneLine = NEW_LINE !in longestPaddedScramble + val fontSize = PdfUtil.fitText(Font(FontUtil.MONO_FONT), longestScramble, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, true) + val fontSizeIfIncludingNewlines = availableArea.height / maxLines - // I don't know how to configure ColumnText.fitText's word wrapping characters, - // so instead, I just replace each character I don't want to wrap with M, which - // should be the widest character (we're using a monospaced font, - // so that doesn't really matter), and won't get wrapped. - val longestScrambleMasked = WIDEST_CHAR_STRING.repeat(longestScramble.length) - val longestPaddedScrambleMasked = longestPaddedScramble.replace(" ".toRegex(), WIDEST_CHAR_STRING) - - val fontSizeWithoutPadding = PdfUtil.fitText(Font(FontUtil.MONO_FONT), longestScrambleMasked, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, false) - - // If the scramble contains newlines, then we *only* allow wrapping at the newlines. - val longestRespectingNewlines = longestPaddedScramble.takeIf { longestScrambleOneLine } ?: longestPaddedScrambleMasked - val fontSizeIfIncludingNewlines = PdfUtil.fitText(Font(FontUtil.MONO_FONT), longestRespectingNewlines, availableArea, FontUtil.MAX_SCRAMBLE_FONT_SIZE, true) - - val oneLine = longestScrambleOneLine && fontSizeWithoutPadding >= FontUtil.MINIMUM_ONE_LINE_FONT_SIZE - val perfectFontSize = fontSizeWithoutPadding.takeIf { oneLine } ?: fontSizeIfIncludingNewlines + // 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) return Font(FontUtil.MONO_FONT, perfectFontSize, Font.NORMAL) } @@ -175,7 +163,6 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) const val SCRAMBLE_IMAGE_PADDING = 2 const val SCRAMBLE_MARGIN = 5 - const val WIDEST_CHAR_STRING = Typography.nbsp.toString() const val EMPTY_CELL_CONTENT = "" @@ -195,26 +182,5 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) const val HORIZONTAL_MARGIN = 35f const val NEW_LINE = "\n" - - @JvmStatic - fun main(args: Array) { - val rawScrambles = listOf( - "F' D2 B2 Dw' Fw' U2 Dw Fw Lw' D2 Dw Bw' B' L Lw D' Lw B' F Fw' Rw' Uw D Bw2 Uw D U Rw' L2 B L2 R2 F' D F B2 Dw2 B' Dw Uw' B' Rw2 Lw' Uw' Fw Bw' R2 B' Uw Dw R' Uw' Lw2 R2 B2 Rw B' Bw' Rw2 R2 3Fw 3Uw2", - "Fw2 Uw2 Fw' R U2 R Bw U' Fw2 U' Rw Fw2 Uw' Bw2 B2 F2 Rw2 F' Fw D2 Fw' Uw2 R' Rw F Fw' R L2 Rw2 Uw R2 Dw' F' U2 Uw2 F2 L' U Dw' Rw2 Bw2 Dw R' F' R F' L' U2 Dw R L Rw' D F2 L2 D F B L2 F' 3Uw", - "Lw D2 F' U R' Dw2 L Fw F R2 Uw' Dw L' R' Rw U2 Fw' Dw2 Fw' U Bw2 Uw Bw2 B' Fw' Rw Dw2 F2 Uw Dw B R2 F B' L2 U' R' L Lw2 Uw2 Fw' U Rw Lw2 R' Bw2 Lw B' Rw Uw F D' Uw Dw Bw Lw R Uw U2 3Rw' 3Uw", - "D' F2 Uw2 Fw Rw' Bw L2 Uw L2 Fw2 Dw2 D Uw2 B R' F' Rw2 D' U' Bw2 Dw B' U Rw2 R2 Fw D Dw2 Bw2 Fw' Lw' Fw Dw' F' U Bw Uw2 Bw R Rw U' Bw2 Lw' Rw' Uw Lw' Uw2 Bw Fw' Dw Bw F Lw' Dw U2 Fw2 Dw Fw2 U2 Rw2 3Fw", - "Rw2 L Uw Rw' Fw' Rw' F Fw2 U2 D R U B' U2 B2 F' Lw' F Lw2 L2 Rw2 Fw' D2 Dw' U' Lw' B D' Fw' U Uw' Fw L' F2 R2 D2 B U D2 L B2 D' F2 Dw D Rw D' Lw F' U' Uw2 Fw' Bw' R Lw' F Lw2 Rw Fw2 F 3Rw2 3Uw" - ) - - val scrambles = rawScrambles.map { Scramble(it) } - val scrambleSet = ScrambleSet(42, scrambles, emptyList()) - - val activityCode = ActivityCode.compile(EventData.FIVE_BLD, 0, 0, 0) - - val pdfSheet = GeneralScrambleSheet(scrambleSet, activityCode) - val pdfBytes = pdfSheet.render() - - File(File(System.getProperty("user.home")), "TNoodleDummy.pdf").writeBytes(pdfBytes) - } } } From 82e80cc863f51ef53348418ecf766b4b27d41eca Mon Sep 17 00:00:00 2001 From: Gregor Billing Date: Wed, 7 Apr 2021 01:43:31 +0200 Subject: [PATCH 3/3] Improve font size detection --- .../webscrambles/pdf/GeneralScrambleSheet.kt | 55 ++++++++++++++----- .../server/webscrambles/pdf/util/PdfUtil.kt | 2 +- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt b/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt index 2de792362..d6f0a563a 100644 --- a/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt +++ b/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/GeneralScrambleSheet.kt @@ -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 @@ -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 @@ -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 @@ -78,20 +83,39 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) private fun getFontConfiguration(availableArea: Rectangle, scrambles: List): 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): Boolean { val lineChunks = scrambles.map { it.splitToLineChunks(scrambleFont, scrambleColumnWidth) } @@ -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 { @@ -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) @@ -153,9 +180,9 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) } companion object { - private fun List.toPDFStrings(puzzleName: String) = + private fun List.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 @@ -163,6 +190,8 @@ class GeneralScrambleSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode) 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 = "" @@ -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" } diff --git a/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/util/PdfUtil.kt b/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/util/PdfUtil.kt index e866b115c..4b2433e76 100644 --- a/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/util/PdfUtil.kt +++ b/webscrambles/src/main/kotlin/org/worldcubeassociation/tnoodle/server/webscrambles/pdf/util/PdfUtil.kt @@ -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) }