From 3bb2b58ce1ac9eadff9afbcfe8f9b536a8fa850f Mon Sep 17 00:00:00 2001 From: sathwikcs <85106054+sathwikcs@users.noreply.github.com> Date: Fri, 17 Jan 2025 16:35:52 +0530 Subject: [PATCH] Updated the pdf generator, and improved pdf design --- .../sminnovations/smiadmin/ui/PDFGenerator.kt | 335 +++++++++++------- 1 file changed, 210 insertions(+), 125 deletions(-) diff --git a/app/src/main/java/in/sminnovations/smiadmin/ui/PDFGenerator.kt b/app/src/main/java/in/sminnovations/smiadmin/ui/PDFGenerator.kt index 23175b1..d0bd11d 100644 --- a/app/src/main/java/in/sminnovations/smiadmin/ui/PDFGenerator.kt +++ b/app/src/main/java/in/sminnovations/smiadmin/ui/PDFGenerator.kt @@ -1,19 +1,23 @@ package `in`.sminnovations.smiadmin.ui import android.content.Context +import android.graphics.Color +import android.graphics.LinearGradient +import android.graphics.Paint +import android.graphics.Path +import android.graphics.Shader +import android.graphics.Typeface import android.graphics.pdf.PdfDocument import android.os.Environment -import java.io.File -import java.io.FileOutputStream -import android.graphics.Paint -import android.graphics.Typeface -import android.graphics.Color import com.google.firebase.Firebase import com.google.firebase.firestore.firestore import `in`.sminnovations.smiadmin.data.Employee import kotlinx.coroutines.tasks.await +import java.io.File +import java.io.FileOutputStream import java.text.SimpleDateFormat -import java.util.* +import java.util.Calendar +import java.util.Locale class PDFGenerator( private val context: Context, @@ -23,16 +27,22 @@ class PDFGenerator( private lateinit var currentPage: PdfDocument.Page private lateinit var canvas: android.graphics.Canvas private var currentY: Float = 0f - private val startX = 40f - private val pageHeight = 842f // A4 height - private val pageWidth = 595f // A4 width - private var pageNumber = 1 - // Define colors - private val primaryColor = Color.rgb(41, 128, 185) // Blue - private val secondaryColor = Color.rgb(44, 62, 80) // Dark Blue - private val borderColor = Color.rgb(189, 195, 199) // Light Gray - private val backgroundColor = Color.rgb(236, 240, 241) // Very Light Gray + // Constants for page layout + private val pageWidth = 595f // A4 width + private val pageHeight = 842f // A4 height + private val margin = 40f + private val contentWidth = pageWidth - (2 * margin) + + // Professional color scheme + private val primaryColor = Color.rgb(41, 128, 185) // Professional blue + private val secondaryColor = Color.rgb(44, 62, 80) // Dark blue + private val accentColor = Color.rgb(230, 126, 34) // Orange for highlights + private val backgroundColor = Color.rgb(236, 240, 241) // Light gray + private val borderColor = Color.rgb(189, 195, 199) // Medium gray + private val textColor = Color.rgb(52, 73, 94) // Dark gray for text + + private var pageNumber = 1 suspend fun generateAttendanceReport( employees: List, @@ -42,35 +52,43 @@ class PDFGenerator( document = PdfDocument() createNewPage() - // Setup paints - val headerPaint = Paint().apply { - textSize = 28f - textAlign = Paint.Align.CENTER - typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) + // Create paint objects + val titlePaint = Paint().apply { + textSize = 32f + typeface = Typeface.create("sans-serif-light", Typeface.BOLD) color = primaryColor + textAlign = Paint.Align.CENTER } - val subHeaderPaint = Paint().apply { - textSize = 20f - textAlign = Paint.Align.CENTER + val subtitlePaint = Paint().apply { + textSize = 24f + typeface = Typeface.create("sans-serif-light", Typeface.NORMAL) color = secondaryColor + textAlign = Paint.Align.CENTER } val tableHeaderPaint = Paint().apply { textSize = 14f - textAlign = Paint.Align.LEFT - typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) + typeface = Typeface.create("sans-serif-medium", Typeface.BOLD) color = Color.WHITE } - val tablePaint = Paint().apply { + val regularPaint = Paint().apply { textSize = 12f - textAlign = Paint.Align.LEFT - color = secondaryColor + typeface = Typeface.create("sans-serif", Typeface.NORMAL) + color = textColor } - // Draw headers and table - drawPageHeaders(headerPaint, subHeaderPaint, selectedYear, selectedMonth) + val highlightPaint = Paint().apply { + textSize = 12f + typeface = Typeface.create("sans-serif-medium", Typeface.BOLD) + color = accentColor + } + + // Draw report header + drawReportHeader(selectedYear, selectedMonth, titlePaint, subtitlePaint) + + // Draw table headers drawTableHeaders(tableHeaderPaint) // Process each employee @@ -78,7 +96,6 @@ class PDFGenerator( if (currentY > pageHeight - 100) { finishCurrentPage() createNewPage() - drawPageHeaders(headerPaint, subHeaderPaint, selectedYear, selectedMonth) drawTableHeaders(tableHeaderPaint) } @@ -98,58 +115,166 @@ class PDFGenerator( } .sorted() - // Draw row background - val rowPaint = Paint().apply { - color = if ((employees.indexOf(employee) % 2) == 0) { - backgroundColor - } else { - Color.WHITE - } - } - - val rowHeight = 60f - canvas.drawRect(startX, currentY, pageWidth - startX, currentY + rowHeight, rowPaint) - - // Draw employee details - val nameY = currentY + 25f - canvas.drawText(employee.Name, startX + 10f, nameY, tablePaint) - canvas.drawText( - "${presentDates.size} days", - startX + 190f, - nameY, - tablePaint - ) - - // Format dates in groups of 5 - val datesText = presentDates.chunked(5).joinToString("\n") { chunk -> - chunk.joinToString(", ") { day -> String.format("%02d", day) } - } - - // Draw dates in multiple lines if needed - var dateY = nameY - datesText.split("\n").forEach { line -> - canvas.drawText(line, startX + 290f, dateY, tablePaint) - dateY += 15f - } - - // Draw row borders - val borderPaint = Paint().apply { - style = Paint.Style.STROKE - strokeWidth = 0.5f - color = borderColor - } - canvas.drawLine( - startX, currentY + rowHeight, - pageWidth - startX, currentY + rowHeight, - borderPaint - ) - - currentY += rowHeight + drawEmployeeRow(employee, presentDates, regularPaint, highlightPaint) } + // Add footer with page numbers + drawFooter(regularPaint) + // Finish and save document finishCurrentPage() + return saveDocument(selectedYear, selectedMonth) + } + private fun drawReportHeader( + selectedYear: Int, + selectedMonth: Int, + titlePaint: Paint, + subtitlePaint: Paint + ) { + // Draw decorative header background + val headerPath = Path().apply { + moveTo(0f, 0f) + lineTo(pageWidth, 0f) + lineTo(pageWidth, 120f) + quadTo(pageWidth/2, 140f, 0f, 120f) + close() + } + + canvas.drawPath(headerPath, Paint().apply { + color = primaryColor + alpha = 20 + }) + + // Draw company name + canvas.drawText(companyName, pageWidth/2, 70f, titlePaint) + + // Draw report title + val monthName = SimpleDateFormat("MMMM", Locale.getDefault()) + .format(Calendar.getInstance().apply { + set(Calendar.MONTH, selectedMonth - 1) + }.time) + + canvas.drawText( + "Monthly Attendance Summary", + pageWidth/2, + 105f, + subtitlePaint + ) + + canvas.drawText( + "$monthName $selectedYear", + pageWidth/2, + 135f, + subtitlePaint + ) + + currentY = 180f + } + + private fun drawTableHeaders(headerPaint: Paint) { + val headerHeight = 45f + + // Create gradient for header background + val gradient = LinearGradient( + margin, currentY, + margin, currentY + headerHeight, + primaryColor, + secondaryColor, + Shader.TileMode.CLAMP + ) + + // Draw header background with gradient + canvas.drawRoundRect( + margin, currentY, + pageWidth - margin, + currentY + headerHeight, + 8f, 8f, + Paint().apply { shader = gradient } + ) + + // Draw column headers + var xPos = margin + 15f + canvas.drawText("Employee Name", xPos, currentY + 30f, headerPaint) + + xPos += 180f + canvas.drawText("Days Present", xPos, currentY + 30f, headerPaint) + + xPos += 100f + canvas.drawText("Attendance Dates", xPos, currentY + 30f, headerPaint) + + currentY += headerHeight + 5f + } + + private fun drawEmployeeRow( + employee: Employee, + presentDates: List, + regularPaint: Paint, + highlightPaint: Paint + ) { + val rowHeight = 60f + + // Draw row background + canvas.drawRoundRect( + margin, currentY, + pageWidth - margin, + currentY + rowHeight, + 4f, 4f, + Paint().apply { + color = backgroundColor + } + ) + + // Draw employee name + canvas.drawText( + employee.Name, + margin + 15f, + currentY + 35f, + regularPaint + ) + + // Draw days present + canvas.drawText( + "${presentDates.size} days", + margin + 195f, + currentY + 35f, + highlightPaint + ) + + // Draw attendance dates + val datesText = presentDates.chunked(5) + .joinToString("\n") { chunk -> + chunk.joinToString(", ") { + String.format("%02d", it) + } + } + + var dateY = currentY + 25f + datesText.split("\n").forEach { line -> + canvas.drawText( + line, + margin + 295f, + dateY, + regularPaint + ) + dateY += 15f + } + + currentY += rowHeight + 5f + } + + private fun drawFooter(paint: Paint) { + paint.textAlign = Paint.Align.CENTER + paint.textSize = 10f + canvas.drawText( + "Page $pageNumber", + pageWidth/2, + pageHeight - margin/2, + paint + ) + } + + private fun saveDocument(selectedYear: Int, selectedMonth: Int): File { val fileName = "Attendance_Summary_${selectedYear}_${selectedMonth}.pdf" val file = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), @@ -165,58 +290,18 @@ class PDFGenerator( } private fun createNewPage() { - val pageInfo = PdfDocument.PageInfo.Builder(pageWidth.toInt(), pageHeight.toInt(), pageNumber++).create() + val pageInfo = PdfDocument.PageInfo.Builder( + pageWidth.toInt(), + pageHeight.toInt(), + pageNumber++ + ).create() + currentPage = document.startPage(pageInfo) canvas = currentPage.canvas - currentY = 120f + currentY = margin } private fun finishCurrentPage() { document.finishPage(currentPage) } - - private fun drawPageHeaders( - headerPaint: Paint, - subHeaderPaint: Paint, - selectedYear: Int, - selectedMonth: Int - ) { - // Draw company logo background - val logoBgPaint = Paint().apply { - color = primaryColor - alpha = 30 - } - canvas.drawCircle(pageWidth / 2f, 45f, 100f, logoBgPaint) - - // Draw company name - canvas.drawText(companyName, pageWidth / 2f, 60f, headerPaint) - - // Draw report title - val monthName = SimpleDateFormat("MMMM", Locale.getDefault()) - .format(Calendar.getInstance().apply { - set(Calendar.MONTH, selectedMonth - 1) - }.time) - - val reportTitle = "Monthly Attendance Summary - $monthName $selectedYear" - canvas.drawText(reportTitle, pageWidth / 2f, 95f, subHeaderPaint) - currentY = 140f - } - - private fun drawTableHeaders(headerPaint: Paint) { - // Draw header background - val headerBgPaint = Paint().apply { - color = primaryColor - } - - val headerHeight = 40f - canvas.drawRect(startX, currentY, pageWidth - startX, currentY + headerHeight, headerBgPaint) - - // Draw column headers - val textY = currentY + 25f - canvas.drawText("Employee Name", startX + 10f, textY, headerPaint) - canvas.drawText("Present Days", startX + 190f, textY, headerPaint) - canvas.drawText("Dates Present", startX + 290f, textY, headerPaint) - - currentY += headerHeight - } } \ No newline at end of file