From db0c099abec19ca253f21d407793e5a049b38a0f Mon Sep 17 00:00:00 2001 From: andreia Date: Mon, 27 Jul 2026 19:11:40 +0200 Subject: [PATCH 1/4] swap job in the header with submission and make the report submission title bigger --- .../feature/pdf/render/PdfWriterTest.kt | 34 ++++++++-- .../feature/pdf/render/PdfTextPaints.kt | 2 +- .../feature/pdf/render/PdfWriter.kt | 64 ++++++++++--------- .../feature/pdf/mapper/LoiReportMapper.kt | 13 +++- .../pdf/model/SubmissionPdfDocument.kt | 13 +++- .../feature/pdf/render/PdfConfig.kt | 4 +- .../pdf/render/layout/PageHeaderLayout.kt | 6 +- .../feature/pdf/render/layout/TableLayout.kt | 27 ++++++-- .../feature/pdf/PdfExportServiceTest.kt | 7 +- .../feature/pdf/mapper/LoiReportMapperTest.kt | 42 ++++++++++++ .../pdf/render/layout/PageHeaderLayoutTest.kt | 20 ++++-- .../pdf/render/layout/TableLayoutTest.kt | 39 +++++++---- 12 files changed, 197 insertions(+), 74 deletions(-) diff --git a/feature/pdf/src/androidHostTest/kotlin/org/groundplatform/feature/pdf/render/PdfWriterTest.kt b/feature/pdf/src/androidHostTest/kotlin/org/groundplatform/feature/pdf/render/PdfWriterTest.kt index e0bae8dc8d..d233c13a7b 100644 --- a/feature/pdf/src/androidHostTest/kotlin/org/groundplatform/feature/pdf/render/PdfWriterTest.kt +++ b/feature/pdf/src/androidHostTest/kotlin/org/groundplatform/feature/pdf/render/PdfWriterTest.kt @@ -59,12 +59,29 @@ class PdfWriterTest { } @Test - fun `draws the header values on the page`() { + fun `draws the survey, submission and date columns in the header`() { val canvas = renderDocument(SINGLE_PAGE_DOCUMENT) - assertTrue(canvas.drawnText.contains(HEADER.surveyName)) - assertTrue(canvas.drawnText.contains(HEADER.jobName)) - assertTrue(canvas.drawnText.contains(HEADER.timestamp)) + assertTrue(canvas.drawnText.containsAll(listOf(HEADER.surveyLabel, HEADER.surveyName))) + assertTrue(canvas.drawnText.containsAll(listOf(HEADER.submissionLabel, HEADER.submissionName))) + assertTrue(canvas.drawnText.containsAll(listOf(HEADER.dateLabel, HEADER.timestamp))) + } + + @Test + fun `draws no job column in the header`() { + val canvas = renderDocument(SINGLE_PAGE_DOCUMENT) + + assertFalse(canvas.drawnText.contains(TABLE.jobName)) + assertEquals(1, canvas.drawnText.count { it == "${TABLE.jobLabel}: ${TABLE.jobName}" }) + } + + @Test + fun `draws the submission heading above the table with the job below it`() { + val canvas = renderDocument(SINGLE_PAGE_DOCUMENT) + + val jobLineIndex = canvas.drawnText.indexOf("${TABLE.jobLabel}: ${TABLE.jobName}") + assertTrue(jobLineIndex > 0) + assertEquals("${TABLE.submissionLabel}: ${TABLE.loiName}", canvas.drawnText[jobLineIndex - 1]) } @Test @@ -187,7 +204,7 @@ class PdfWriterTest { val canvas = renderDocument(tableless, pdfImageSet(qr = pdfImage())) assertEquals(listOf(1), canvas.startedPages) - assertFalse(canvas.drawnText.contains(TABLE.submissionLabel)) + assertFalse(canvas.drawnText.contains("${TABLE.submissionLabel}: ${TABLE.loiName}")) } private fun renderDocument( @@ -241,8 +258,9 @@ class PdfWriterTest { SubmissionPdfDocument.Header( surveyLabel = "Survey", surveyName = "Survey name", - jobLabel = "Job", - jobName = "Job name", + submissionLabel = "Submission", + submissionName = "Plot 42", + dateLabel = "Date", timestamp = "timestamp", ) val FOOTER = @@ -258,6 +276,8 @@ class PdfWriterTest { SubmissionPdfDocument.Table( submissionLabel = "Submission", loiName = "Plot 42", + jobLabel = "Job", + jobName = "Job name", rows = emptyList(), ) diff --git a/feature/pdf/src/androidMain/kotlin/org/groundplatform/feature/pdf/render/PdfTextPaints.kt b/feature/pdf/src/androidMain/kotlin/org/groundplatform/feature/pdf/render/PdfTextPaints.kt index be8defef4b..feee0c6bdd 100644 --- a/feature/pdf/src/androidMain/kotlin/org/groundplatform/feature/pdf/render/PdfTextPaints.kt +++ b/feature/pdf/src/androidMain/kotlin/org/groundplatform/feature/pdf/render/PdfTextPaints.kt @@ -23,7 +23,7 @@ import org.groundplatform.feature.pdf.render.PdfConfig.CAPTION_SIZE import org.groundplatform.feature.pdf.render.PdfConfig.TITLE_SIZE internal class PdfTextPaints { - val title: TextPaint = textPaint(TITLE_SIZE, bold = false) + val title: TextPaint = textPaint(TITLE_SIZE, bold = true) val body: TextPaint = textPaint(BODY_SIZE, bold = false) val metaLabel: TextPaint = textPaint(CAPTION_SIZE, bold = true, textColor = Color.GRAY) val meta: TextPaint = textPaint(CAPTION_SIZE, bold = false, textColor = Color.GRAY) diff --git a/feature/pdf/src/androidMain/kotlin/org/groundplatform/feature/pdf/render/PdfWriter.kt b/feature/pdf/src/androidMain/kotlin/org/groundplatform/feature/pdf/render/PdfWriter.kt index d23bfe5a12..b13ed0823a 100644 --- a/feature/pdf/src/androidMain/kotlin/org/groundplatform/feature/pdf/render/PdfWriter.kt +++ b/feature/pdf/src/androidMain/kotlin/org/groundplatform/feature/pdf/render/PdfWriter.kt @@ -97,20 +97,19 @@ internal class PdfWriter( private fun drawTable(table: SubmissionPdfDocument.Table) { val rows = table.rows.takeIf { it.isNotEmpty() } ?: return pageController.ensurePage() - val label = - SpannableString("${table.submissionLabel}: ${table.loiName}").apply { - setSpan( - StyleSpan(Typeface.BOLD), - 0, - table.submissionLabel.length, - Spanned.SPAN_INCLUSIVE_EXCLUSIVE, - ) - } - val labelLayout = staticLayout(label, paints.title, USABLE_WIDTH) - val tableLabel = - TableLayout.getLabel(top = cursor.y, labelHeight = labelLayout.height.toFloat()) - drawStaticLayoutAt(labelLayout, tableLabel.labelOffset) - cursor.moveTo(tableLabel.nextCursorY) + val titleLayout = + staticLayout("${table.submissionLabel}: ${table.loiName}", paints.title, USABLE_WIDTH) + val subtitleLayout = + staticLayout(labeled(table.jobLabel, table.jobName), paints.body, USABLE_WIDTH) + val heading = + TableLayout.getHeading( + top = cursor.y, + titleHeight = titleLayout.height.toFloat(), + subtitleHeight = subtitleLayout.height.toFloat(), + ) + drawStaticLayoutAt(titleLayout, heading.titleOffset) + drawStaticLayoutAt(subtitleLayout, heading.subtitleOffset) + cursor.moveTo(heading.nextCursorY) rows.forEach { row -> when (val answer = row.answer) { is Answer.Text -> @@ -143,11 +142,16 @@ internal class PdfWriter( maxWidth = columnWidth, maxLines = PageHeaderLayout.MAX_LINES, ) - val jobLabel = - staticLayout(header.jobLabel, paints.metaLabel, columnWidth, Layout.Alignment.ALIGN_CENTER) - val jobValue = + val submissionLabel = staticLayout( - text = header.jobName, + header.submissionLabel, + paints.metaLabel, + columnWidth, + Layout.Alignment.ALIGN_CENTER, + ) + val submissionValue = + staticLayout( + text = header.submissionName, paint = paints.meta, maxWidth = columnWidth, alignment = Layout.Alignment.ALIGN_CENTER, @@ -171,9 +175,9 @@ internal class PdfWriter( drawStaticLayoutAt(surveyLabel, layout.leftColumn.labelOffset) drawStaticLayoutAt(surveyValue, layout.leftColumn.valueOffset) - drawStaticLayoutAt(jobLabel, layout.centerColumn.labelOffset) - drawStaticLayoutAt(jobValue, layout.centerColumn.valueOffset) - drawStaticLayoutAt(timestamp, layout.rightTextOffset) + drawStaticLayoutAt(submissionLabel, layout.centerColumn.labelOffset) + drawStaticLayoutAt(submissionValue, layout.centerColumn.valueOffset) + drawStaticLayoutAt(timestamp, layout.rightColumn.valueOffset) cursor.moveTo(layout.nextCursorY) } @@ -238,19 +242,19 @@ internal class PdfWriter( private fun drawLine(line: PdfLine) = pdfCanvas.drawLine(line.startX, line.startY, line.endX, line.endY) - private fun buildFooterLayout(footer: Footer): StaticLayout { - val footerLabel = footer.dataCollectorLabel - val footerText = - SpannableString("$footerLabel: ${footer.dataCollectorName}, ${footer.userEmail}").apply { - setSpan(StyleSpan(Typeface.BOLD), 0, footerLabel.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) - } - return staticLayout( - footerText, + private fun buildFooterLayout(footer: Footer): StaticLayout = + staticLayout( + labeled(footer.dataCollectorLabel, "${footer.dataCollectorName}, ${footer.userEmail}"), paints.meta, PageFooterLayout.TEXT_MAX_WIDTH, maxLines = PageFooterLayout.MAX_LINES, ) - } + + /** Builds a `"