Skip to content

Add Android rendering logic for PDF reports#3774

Draft
andreia-ferreira wants to merge 15 commits into
masterfrom
andreia/3739/pdf-report-layout
Draft

Add Android rendering logic for PDF reports#3774
andreia-ferreira wants to merge 15 commits into
masterfrom
andreia/3739/pdf-report-layout

Conversation

@andreia-ferreira

@andreia-ferreira andreia-ferreira commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Towards #3715

This PR adds the specific Android implementations for rendering PDF files.

Main changes:

  • New helpers:
    • PdfCursor to keep track of the current position within a page and its reserved spaces
    • PdfPageController to keep track of page counts and their lifecycles
  • Android implementations of the previously introduced interfaces as well as platform specific helpers PdfWriter, PdfCanvas, PdfTextPaints and DocumentPdfCanvas in order to draw the layout

There are no functionality changes yet. The full flow will be implemented in a final follow up PR. For reference, the current layout will generate the following document structure:
Screenshot From 2026-06-10 14-25-22

@shobhitagarwal1612 PTAL?

@andreia-ferreira andreia-ferreira changed the title Andreia/3739/pdf report layout Add components to render the PDF layout Jun 9, 2026
@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.66507% with 85 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.91%. Comparing base (b8ccfab) to head (b7d18a6).

Files with missing lines Patch % Lines
...undplatform/feature/pdf/AndroidPdfImageProvider.kt 46.03% 25 Missing and 9 partials ⚠️
...ndplatform/feature/pdf/AndroidPdfReportLauncher.kt 0.00% 19 Missing ⚠️
...g/groundplatform/feature/pdf/AndroidPdfRenderer.kt 0.00% 17 Missing ⚠️
...ndplatform/feature/pdf/render/DocumentPdfCanvas.kt 55.55% 6 Missing and 2 partials ⚠️
...roundplatform/ui/components/qrcode/GroundQrCode.kt 0.00% 3 Missing ⚠️
...org/groundplatform/feature/pdf/render/PdfWriter.kt 97.81% 1 Missing and 2 partials ⚠️
...ndplatform/feature/pdf/AndroidPdfOutputProvider.kt 88.88% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3774      +/-   ##
============================================
+ Coverage     66.14%   66.91%   +0.77%     
- Complexity     1660     1770     +110     
============================================
  Files           386      402      +16     
  Lines         10039    10454     +415     
  Branches       1288     1326      +38     
============================================
+ Hits           6640     6995     +355     
- Misses         2722     2768      +46     
- Partials        677      691      +14     
Files with missing lines Coverage Δ
...rm/ui/components/qrcode/QrCodeGenerator.android.kt 100.00% <ø> (+100.00%) ⬆️
...ndplatform/ui/components/qrcode/QrCodeGenerator.kt 100.00% <100.00%> (ø)
...org/groundplatform/feature/pdf/render/PdfCanvas.kt 100.00% <100.00%> (ø)
...groundplatform/feature/pdf/render/PdfTextPaints.kt 100.00% <100.00%> (ø)
...org/groundplatform/feature/pdf/render/PdfCursor.kt 100.00% <100.00%> (ø)
...g/groundplatform/feature/pdf/render/PdfGeometry.kt 100.00% <100.00%> (ø)
...ndplatform/feature/pdf/render/PdfPageController.kt 100.00% <100.00%> (ø)
.../feature/pdf/render/components/PageFooterLayout.kt 100.00% <100.00%> (ø)
.../feature/pdf/render/components/PageHeaderLayout.kt 100.00% <100.00%> (ø)
...orm/feature/pdf/render/components/QrBlockLayout.kt 100.00% <100.00%> (ø)
... and 8 more

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@andreia-ferreira andreia-ferreira force-pushed the andreia/3739/pdf-report-layout branch from 0c304e7 to d7bd17c Compare June 9, 2026 15:26

@shobhitagarwal1612 shobhitagarwal1612 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add screenshots to the description?

@andreia-ferreira

Copy link
Copy Markdown
Collaborator Author

@shobhitagarwal1612 there is no UI entry point in this PR to show the direct output. But I've added a screenshot of how the doc will look like once the feature is completed in the next PR, hope it helps

@shobhitagarwal1612 shobhitagarwal1612 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sending the PR. I've left a few comments for you to review. On a high level, the PR looks reasonable but due to the large diff, it's hard to throughly review the code. Would you mind breaking it up into small 2-3 PRs?

Comment on lines +45 to +47
writer(document, images, DocumentPdfCanvas(pdf), totalPages = totalPages)
.drawDocument(document)
File(outputPath).outputStream().use { pdf.writeTo(it) }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is file operation, should it be wrapped in IO dispatchers block? Or will this be handled at the caller?

Comment on lines +70 to +77
photoFilenames
.filter { it.isNotEmpty() }
.forEach { filename ->
loadPhotoBitmap(filename)?.let { bitmap ->
bitmapsToRelease += bitmap
images[PdfImageSet.ImageRef.Photo(filename)] = PdfImage(bitmap)
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use async / awaitAll to decode the images concurrently?

private val photoMaxWidthPx = pointsToRenderPixels(PdfConfig.TABLE_ANSWER_TEXT_WIDTH.toFloat())
private val photoMaxHeightPx = pointsToRenderPixels(PdfConfig.PHOTO_MAX_HEIGHT.toFloat())

override suspend fun load(qrContent: String?, photoFilenames: Set<String>): PdfImageSet {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to previous question, are we planning to wrap the caller with io dispatcher? If not, should we do it here?

Comment on lines +193 to +195
val answerLayout =
if (answerText.isEmpty()) null
else staticLayout(answerText, paints.body, PdfConfig.TABLE_ANSWER_TEXT_WIDTH)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can be replaced with ?. operator

val answerLayout = answerText.takeIf { it.isNotEmpty() }
    ?.let { staticLayout(it, paints.body, PdfConfig.TABLE_ANSWER_TEXT_WIDTH) }

Comment on lines +139 to +140
while (width / (sampleSize * 2) >= maxWidth || height / (sampleSize * 2) >= maxHeight) {
sampleSize *= 2

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this is hard to read. Can we either use local variable or helper methods?

@andreia-ferreira andreia-ferreira changed the title Add components to render the PDF layout Add Android rendering logic for PDF reports Jun 10, 2026
@andreia-ferreira

Copy link
Copy Markdown
Collaborator Author

extracted 2 PRs out of this one:

This way, this PR becomes focused on the Android implementation of the interfaces needed to generate the PDF report. Since his depends on the PRs above, I'll mark this as draft for the time being

@andreia-ferreira andreia-ferreira marked this pull request as draft June 10, 2026 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants