Updated the pdf generator, and improved pdf design
This commit is contained in:
@@ -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<Employee>,
|
||||
@@ -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<Int>,
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user