Skip to content

Commit

Permalink
Merge 9ba6d8d into 3bf4478
Browse files Browse the repository at this point in the history
  • Loading branch information
Gregor Billing committed Apr 3, 2021
2 parents 3bf4478 + 9ba6d8d commit f958981
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ package org.worldcubeassociation.tnoodle.server.webscrambles.pdf

import com.itextpdf.text.Document
import com.itextpdf.text.pdf.PdfWriter
import org.worldcubeassociation.tnoodle.server.model.EventData
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.model.*
import java.util.*

class FmcGenericSolutionSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode, competitionTitle: String, locale: Locale) : FmcSolutionSheet(scrambleSet, activityCode, competitionTitle, locale, true) {
class FmcGenericSolutionSheet(competitionTitle: String, locale: Locale) : FmcSolutionSheet(ScrambleSet.empty(), PSEUDO_ACTIVITY_CODE, competitionTitle, locale, true) {
override fun PdfWriter.writeContents(document: Document) {
addFmcSolutionSheet(document, INDEX_SKIP_SCRAMBLE)
}

companion object {
const val INDEX_SKIP_SCRAMBLE = -1

val PSEUDO_ACTIVITY_CODE = ActivityCode.compile(EventData.THREE_FM, 0, 0, 0)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,31 @@ class FmcScrambleCutoutSheet(scrambleSet: ScrambleSet, activityCode: ActivityCod
val scrambleSuffix = Translate.translate("fmc.scrambleXofY", locale, substitutions)
.takeIf { expectedAttemptNum > 1 } ?: ""

val attemptTitle = activityCode.compileTitleString(locale, includeGroupID = hasGroupID)
val title = "$competitionTitle - $attemptTitle$scrambleSuffix"
val eventTitle = Translate.translate("fmc.event", locale)
val attemptDetails = activityCode.compileTitleString(locale, includeEvent = false, includeGroupID = hasGroupID)
val attemptTitle = "$eventTitle $attemptDetails".trim()

val title = "$attemptTitle $scrambleSuffix"

val font = Font(BASE_FONT, FONT_SIZE / 2)
val localBaseFont = FontUtil.getFontForLocale(locale)
val localFont = Font(localBaseFont, FONT_SIZE)

// empty strings for space above and below
val textList = listOf("", title, scramble, "")
val alignList = List(textList.size) { Element.ALIGN_LEFT }
val textList = listOf(
"" to font,
competitionTitle to font,
title to localFont,
scramble to localFont,
"" to font)

val paddedTitleItems = textList.zip(alignList)
val alignList = List(textList.size) { Element.ALIGN_LEFT }

val font = Font(BASE_FONT, FONT_SIZE)
val paddedTitleItems = textList.zipTriple(alignList)

for (i in 0 until SCRAMBLES_PER_SHEET) {
val rect = Rectangle(LEFT.toFloat(), (top - i * availableScrambleHeight).toFloat(), (right - dim.width - SPACE_SCRAMBLE_IMAGE).toFloat(), (top - (i + 1) * availableScrambleHeight).toFloat())
directContent.populateRect(rect, paddedTitleItems, font)
directContent.populateRect(rect, paddedTitleItems)

directContent.addImage(Image.getInstance(tp), dim.width.toDouble(), 0.0, 0.0, dim.height.toDouble(), (right - dim.width).toDouble(), top.toDouble() - (i + 1) * availableScrambleHeight + (availableScrambleHeight - dim.getHeight()) / 2)

Expand All @@ -82,5 +93,9 @@ class FmcScrambleCutoutSheet(scrambleSet: ScrambleSet, activityCode: ActivityCod
val FONT_SIZE = 20f

val SCRAMBLES_PER_SHEET = 8

private fun <A,B,C> Iterable<Pair<A,B>>.zipTriple(other: Iterable<C>): List<Triple<A,B,C>> {
return zip(other).map { Triple(it.first.first, it.first.second, it.second) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,17 @@ open class FmcSolutionSheet(scrambleSet: ScrambleSet, activityCode: ActivityCode
val gradeRect = Rectangle((competitorInfoLeft + margin).toFloat(), competitorInfoBottom.toFloat(), (right - margin).toFloat(), gradeBottom.toFloat())
val scrambleImageRect = Rectangle((competitorInfoLeft + margin).toFloat(), gradeBottom.toFloat(), (right - margin).toFloat(), scrambleBorderTop.toFloat())

cb.fitAndShowText(Translate.translate("fmc.event", locale), titleRect, titleFont, Element.ALIGN_CENTER)
val localEventTitle = Translate.translate("fmc.event", locale)
cb.fitAndShowText(localEventTitle, titleRect, titleFont, Element.ALIGN_CENTER)

// Both competitor com competition details
val compDetailItems = mutableListOf<Pair<String, Int>>()

if (withScramble) {
val activityTitle = activityCode.copyParts(attemptNumber = null)
.compileTitleString(locale, includeGroupID = hasGroupID)
val activityTitleRaw = activityCode.copyParts(attemptNumber = null)
.compileTitleString(locale, includeEvent = false, includeGroupID = hasGroupID)

val activityTitle = "$localEventTitle $activityTitleRaw"

compDetailItems.add(competitionTitle to Element.ALIGN_CENTER)
compDetailItems.add(activityTitle to Element.ALIGN_CENTER)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ object PdfDrawUtil {
}

fun PdfContentByte.fitAndShowText(text: String, rect: Rectangle, font: Font, align: Int = Element.ALIGN_LEFT, leadingMultiplier: Float = 1f): Int {
val approxFontSize = PdfUtil.binarySearchDec(1f, font.size, 1f) {
val approxFontSize = PdfUtil.binarySearchDec(1f, font.size, PdfUtil.FITTEXT_FONTSIZE_PRECISION) {
val iterFont = Font(font.baseFont, it)

// We create a temp pdf and check if the text fit in a rectangle there.
Expand Down Expand Up @@ -93,17 +93,22 @@ object PdfDrawUtil {
}

fun PdfContentByte.populateRect(rect: Rectangle, itemsWithAlignment: List<Pair<String, Int>>, font: Font, leadingMultiplier: Float = 1f) {
val totalHeight = rect.height
val width = rect.width
val mergedItemsWithAlignment = itemsWithAlignment.map { Triple(it.first, font, it.second) }
populateRect(rect, mergedItemsWithAlignment, leadingMultiplier)
}

val x = rect.left
val y = rect.top
fun PdfContentByte.populateRect(rect: Rectangle, itemsWithAlignment: List<Triple<String, Font, Int>>, leadingMultiplier: Float = 1f) {
val fontPoints = itemsWithAlignment.map { it.second.size }
val totalFontPoints = fontPoints.sum()

val height = totalHeight / itemsWithAlignment.size
val slotHeights = fontPoints.map { it / totalFontPoints }.map { it * rect.height }

for ((i, content) in itemsWithAlignment.withIndex()) {
val temp = Rectangle(x, y + height * i - totalHeight - font.size, x + width, y + height * i - totalHeight)
fitAndShowText(content.first, temp, font, content.second, leadingMultiplier)
val height = slotHeights.subList(0, i).sum()
val proportionalFontSize = -slotHeights[i]

val temp = Rectangle(rect.left, rect.bottom + height - proportionalFontSize, rect.right, rect.bottom + height)
fitAndShowText(content.first, temp, content.second, content.third, leadingMultiplier)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ object PdfUtil {
append("\n")
}

private const val FITTEXT_FONTSIZE_PRECISION = 0.1f
const val FITTEXT_FONTSIZE_PRECISION = 0.1f

/**
* Adapted from ColumnText.java in the itextpdf 5.3.0 source code.
Expand All @@ -127,10 +127,8 @@ object PdfUtil {
*/
fun fitText(font: Font, text: String, availableArea: Rectangle, maxFontSize: Float, newlinesAllowed: Boolean, leadingMultiplier: Float = 1f): Float {
return binarySearchDec(1f, maxFontSize, FITTEXT_FONTSIZE_PRECISION) {
// FIXME inplace modification is no good
font.size = it

val lineChunks = text.splitToLineChunks(font, availableArea.width)
val iterFont = Font(font.baseFont, it)
val lineChunks = text.splitToLineChunks(iterFont, availableArea.width)

// The font size seems to be a pretty good estimate for how
// much vertical space a row actually takes up.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.worldcubeassociation.tnoodle.server.webscrambles.zip.folder

import org.slf4j.LoggerFactory
import org.worldcubeassociation.tnoodle.server.model.EventData
import org.worldcubeassociation.tnoodle.server.webscrambles.Translate
import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.FmcGenericSolutionSheet
import org.worldcubeassociation.tnoodle.server.webscrambles.pdf.FmcScrambleCutoutSheet
Expand All @@ -11,13 +10,13 @@ import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.CompetitionDraw
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.ScrambleDrawingData
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.WCIFDataBuilder
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.WCIFDataBuilder.getCachedPdf
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.model.ActivityCode
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.model.Schedule
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.model.ScrambleSet
import org.worldcubeassociation.tnoodle.server.webscrambles.wcif.model.extension.FmcLanguagesExtension
import org.worldcubeassociation.tnoodle.server.webscrambles.zip.folder
import org.worldcubeassociation.tnoodle.server.webscrambles.zip.model.Folder
import java.time.LocalDate
import java.util.*

data class PrintingFolder(val uniqueTitles: Map<String, ScrambleDrawingData>, val globalTitle: String, val wcifSchedule: Schedule) {
val scrambleSheetsFlat = uniqueTitles.values.toList()
Expand All @@ -26,7 +25,6 @@ data class PrintingFolder(val uniqueTitles: Map<String, ScrambleDrawingData>, va
fun assemble(generationDate: LocalDate, versionTag: String, password: String?): Folder {
val fmcRequests = uniqueTitles.filterValues { it.isFmc }

val pseudoActivityCode = ActivityCode.compile(EventData.THREE_FM, round = 1)
val printingCompletePdf = WCIFDataBuilder.requestsToCompletePdf(scrambleDrawingData, generationDate, versionTag, Translate.DEFAULT_LOCALE)

return folder("Printing") {
Expand All @@ -41,35 +39,42 @@ data class PrintingFolder(val uniqueTitles: Map<String, ScrambleDrawingData>, va

if (fmcRequests.isNotEmpty()) {
folder("Fewest Moves - Additional Files") {
val defaultGenericPrintingSheet = FmcGenericSolutionSheet(ScrambleSet.empty(), pseudoActivityCode, globalTitle, Translate.DEFAULT_LOCALE)
val defaultGenericPrintingSheet = FmcGenericSolutionSheet(globalTitle, Translate.DEFAULT_LOCALE)
file("3x3x3 Fewest Moves Solution Sheet.pdf", defaultGenericPrintingSheet.render())

val allDistinctTranslations = fmcRequests.values
.flatMap { it.getTranslationLocales() }.distinct()

if (allDistinctTranslations.isNotEmpty()) {
folder("Translations") {
for (locale in allDistinctTranslations) {
folder(locale.toLanguageTag()) {
val translatedGenericPrintingSheet = FmcGenericSolutionSheet(globalTitle, locale)
file("3x3x3 Fewest Moves Solution Sheet.pdf", translatedGenericPrintingSheet.render())
}
}
}
}

for ((uniq, req) in fmcRequests) {
val defaultCutoutSheet = FmcScrambleCutoutSheet(req.scrambleSet, req.activityCode, globalTitle, Translate.DEFAULT_LOCALE, req.hasGroupID)
file("$uniq - Scramble Cutout Sheet.pdf", defaultCutoutSheet.render(password))

val requestedTranslations = req.scrambleSet.findExtension<FmcLanguagesExtension>()
?.languageTags ?: FMC_LOCALES_BY_TAG.keys

val translationLocales = requestedTranslations.mapNotNull { FMC_LOCALES_BY_TAG[it] }
val translationLocales = req.getTranslationLocales()

if (translationLocales.isNotEmpty()) {
folder("Translations") {
for (locale in translationLocales) {
val languageMarkerTitle = "${locale.toLanguageTag()}/$uniq"

// fewest moves regular sheet
val localPrintingSheet = FmcSolutionSheet(req.scrambleSet, req.activityCode, globalTitle, locale, req.hasGroupID)

// fewest moves generic sheet
val localGenericPrintingSheet = FmcGenericSolutionSheet(req.scrambleSet, req.activityCode, globalTitle, locale)
folder(locale.toLanguageTag()) {
// fewest moves regular sheet
val localPrintingSheet = FmcSolutionSheet(req.scrambleSet, req.activityCode, globalTitle, locale, req.hasGroupID)

// scramble cutout sheet
val localCutoutSheet = FmcScrambleCutoutSheet(req.scrambleSet, req.activityCode, globalTitle, locale, req.hasGroupID)
// scramble cutout sheet
val localCutoutSheet = FmcScrambleCutoutSheet(req.scrambleSet, req.activityCode, globalTitle, locale, req.hasGroupID)

file("$languageMarkerTitle.pdf", localPrintingSheet.render(password))
file("$languageMarkerTitle Solution Sheet.pdf", localGenericPrintingSheet.render(password))
file("$languageMarkerTitle Scramble Cutout Sheet.pdf", localCutoutSheet.render(password))
file("$uniq.pdf", localPrintingSheet.render(password))
file("$uniq Scramble Cutout Sheet.pdf", localCutoutSheet.render(password))
}
}
}
}
Expand Down Expand Up @@ -115,6 +120,13 @@ data class PrintingFolder(val uniqueTitles: Map<String, ScrambleDrawingData>, va
return scheduleMatchedIds.containsAll(requiredIds)
}

private fun ScrambleDrawingData.getTranslationLocales(): List<Locale> {
val requestedTranslations = scrambleSet.findExtension<FmcLanguagesExtension>()
?.languageTags ?: FMC_LOCALES_BY_TAG.keys

return requestedTranslations.mapNotNull { FMC_LOCALES_BY_TAG[it] }
}

val FMC_LOCALES_BY_TAG = Translate.TRANSLATED_LOCALES.associateBy { it.toLanguageTag() }
}
}

0 comments on commit f958981

Please sign in to comment.