Files
alrahma_sunday_school_api/app/Services/Reports/Stickers/StickerPrintService.php
T
2026-03-10 16:40:11 -04:00

256 lines
9.3 KiB
PHP

<?php
namespace App\Services\Reports\Stickers;
use Illuminate\Support\Facades\Log;
class StickerPrintService
{
public function __construct(private StickerQueryService $queryService)
{
}
public function generate(array $payload): array
{
$schoolYear = $this->queryService->resolveSchoolYear($payload['school_year'] ?? null);
if ($schoolYear === '') {
Log::error('Sticker print requested without school year.');
}
$studentId = (int) ($payload['student_id'] ?? 0);
$classSectionId = (int) ($payload['class_section_id'] ?? $payload['class_id'] ?? 0);
$printAll = (bool) ($payload['print_all'] ?? false);
if ($studentId > 0) {
$student = $this->queryService->findStudent($studentId);
if (!$student) {
return ['ok' => false, 'message' => 'Student not found.'];
}
$students = [$student];
} elseif ($classSectionId > 0) {
$students = $this->queryService->listStudentsByClass($classSectionId, $schoolYear);
if (empty($students)) {
return ['ok' => false, 'message' => 'No students found in this class.'];
}
} elseif ($printAll) {
$students = $this->queryService->listStudentsForPrintAll($schoolYear);
if (empty($students)) {
return ['ok' => false, 'message' => 'No students found for this school year.'];
}
} else {
return ['ok' => false, 'message' => 'Please select a student or class.'];
}
$printList = $this->expandCopies($students, $payload, $studentId > 0);
if (empty($printList)) {
return ['ok' => false, 'message' => 'Nothing to print. All quantities are zero.'];
}
$layout = $this->buildLayout($payload);
$pdf = new \FPDF($layout['orientation'], 'mm', $layout['page_size']);
$pdf->SetMargins($layout['margin_left'], $layout['margin_top'], $layout['margin_right']);
$pdf->SetAutoPageBreak(true, $layout['margin_bottom']);
$pdf->AddPage();
$pdf->SetFont('Arial', '', 12);
$pageW = (float) $pdf->GetPageWidth();
$pageH = (float) $pdf->GetPageHeight();
$usableW = max(0.0, $pageW - ($layout['margin_left'] + $layout['margin_right']));
$usableH = max(0.0, $pageH - ($layout['margin_top'] + $layout['margin_bottom']));
$cols = (int) floor(($usableW + max(0.0, $layout['gap_x'])) / (max(0.1, $layout['sticker_width']) + max(0.0, $layout['gap_x'])));
$rows = (int) floor(($usableH + max(0.0, $layout['gap_y'])) / (max(0.1, $layout['sticker_height']) + max(0.0, $layout['gap_y'])));
$cols = max(1, $cols);
$rows = max(1, $rows);
$capacity = $cols * $rows;
if ($layout['stickers_per_page'] > 0 && $layout['stickers_per_page'] < $capacity) {
$rows = (int) ceil($layout['stickers_per_page'] / $cols);
$maxRowsFit = (int) floor(($usableH + max(0.0, $layout['gap_y'])) / (max(0.1, $layout['sticker_height']) + max(0.0, $layout['gap_y'])));
$rows = max(1, min($rows, max(1, $maxRowsFit)));
$capacity = $cols * $rows;
}
$xStart = $layout['margin_left'];
$yStart = $layout['margin_top'];
$x = $xStart;
$y = $yStart;
$ptToMm = static function (float $pt): float {
return $pt * 0.352778;
};
$i = 0;
$perPage = $capacity;
foreach ($printList as $student) {
if ($i > 0 && $i % $perPage === 0) {
$pdf->AddPage();
$x = $xStart;
$y = $yStart;
}
$pdf->SetFillColor(255, 255, 255);
$pdf->Rect($x, $y, $layout['sticker_width'], $layout['sticker_height'], 'F');
$fontFamily = 'Arial';
$fontStyle = 'B';
$maxPt = 16;
$minPt = 8;
$hPad = 2;
$fullName = trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')) ?: 'Student';
$bestPt = $maxPt;
$textW = 0.0;
while ($bestPt >= $minPt) {
$pdf->SetFont($fontFamily, $fontStyle, $bestPt);
$textW = (float) $pdf->GetStringWidth($fullName);
if ($textW <= ($layout['sticker_width'] - 2 * $hPad)) {
break;
}
$bestPt -= 0.5;
}
if ($bestPt < $minPt) {
$bestPt = $minPt;
$pdf->SetFont($fontFamily, $fontStyle, $bestPt);
$textW = (float) $pdf->GetStringWidth($fullName);
}
$textH = $ptToMm($bestPt);
$cellX = $x + (($layout['sticker_width'] - $textW) / 2);
$cellY = $y + (($layout['sticker_height'] - $textH) / 2);
$pdf->SetXY($cellX, $cellY);
$pdf->Cell($textW, $textH, $fullName, 0, 0, 'L');
$i++;
$posInRow = ($i - 1) % $cols;
if ($posInRow === ($cols - 1)) {
$x = $xStart;
$y += $layout['sticker_height'] + $layout['gap_y'];
} else {
$x += $layout['sticker_width'] + $layout['gap_x'];
}
}
return [
'ok' => true,
'pdf' => $pdf->Output('S'),
'filename' => 'Student_Stickers.pdf',
];
}
private function expandCopies(array $students, array $payload, bool $singleStudentMode): array
{
$copiesPosted = $payload['copies'] ?? null;
$singleCopies = (int) ($payload['single_copies'] ?? 1);
if ($singleCopies < 0) {
$singleCopies = 0;
}
$printList = [];
if ($singleStudentMode) {
$qty = max(0, $singleCopies);
for ($k = 0; $k < $qty; $k++) {
$printList[] = $students[0];
}
return $printList;
}
$hasCopies = is_array($copiesPosted);
$defaultQty = $hasCopies ? 0 : 1;
foreach ($students as $student) {
$id = (int) ($student['id'] ?? 0);
$qty = $defaultQty;
if ($hasCopies && array_key_exists((string) $id, $copiesPosted)) {
$qty = (int) $copiesPosted[(string) $id];
}
$qty = max(0, $qty);
for ($k = 0; $k < $qty; $k++) {
$printList[] = $student;
}
}
return $printList;
}
private function buildLayout(array $payload): array
{
$sizeStrReq = trim((string) ($payload['sticker_size'] ?? ''));
$stickerWidth = (float) ($payload['sticker_width'] ?? 0);
$stickerHeight = (float) ($payload['sticker_height'] ?? 0);
if (($stickerWidth <= 0 || $stickerHeight <= 0) && $sizeStrReq !== '') {
if (preg_match('/^\\s*(\\d+(?:\\.\\d+)?)\\s*[xX\\x{00D7}]\\s*(\\d+(?:\\.\\d+)?)\\s*$/u', $sizeStrReq, $m)) {
$stickerWidth = (float) $m[1];
$stickerHeight = (float) $m[2];
}
}
if ($stickerWidth <= 0) {
$stickerWidth = 102.0;
}
if ($stickerHeight <= 0) {
$stickerHeight = 34.0;
}
$stickersPerPage = (int) ($payload['stickers_per_page'] ?? 0);
$pageSize = (string) ($payload['page_size'] ?? 'A4');
$orientation = (string) ($payload['orientation'] ?? 'P');
$marginLeftProvided = array_key_exists('margin_left', $payload);
$marginTopProvided = array_key_exists('margin_top', $payload);
$marginRightProvided = array_key_exists('margin_right', $payload);
$marginBottomProvided = array_key_exists('margin_bottom', $payload);
$gapXProvided = array_key_exists('gap_x', $payload);
$gapYProvided = array_key_exists('gap_y', $payload);
$marginLeft = (float) ($payload['margin_left'] ?? 6);
$marginTop = (float) ($payload['margin_top'] ?? 6);
$marginRight = (float) ($payload['margin_right'] ?? $marginLeft);
$marginBottom = (float) ($payload['margin_bottom'] ?? 10);
$gapX = (float) ($payload['gap_x'] ?? 0);
$gapY = (float) ($payload['gap_y'] ?? 0);
$normalizedSize = strtolower(str_replace(["\x{00D7}", ' '], ['x', ''], $sizeStrReq));
$isXSmallPreset = $normalizedSize === '100x24.5'
|| (abs($stickerWidth - 100.0) < 0.6 && abs($stickerHeight - 24.5) < 0.6);
if ($isXSmallPreset) {
if (!$marginLeftProvided) {
$marginLeft = 4.0;
if (!$marginRightProvided) {
$marginRight = 4.0;
}
}
if (!$marginRightProvided && $marginRight < 4.0) {
$marginRight = 4.0;
}
if (!$gapXProvided) {
$gapX = 0.0;
}
if (!$gapYProvided) {
$gapY = 0.0;
}
}
return [
'sticker_width' => $stickerWidth,
'sticker_height' => $stickerHeight,
'stickers_per_page' => $stickersPerPage,
'page_size' => $pageSize,
'orientation' => $orientation,
'margin_left' => $marginLeft,
'margin_top' => $marginTop,
'margin_right' => $marginRight,
'margin_bottom' => $marginBottom,
'gap_x' => $gapX,
'gap_y' => $gapY,
];
}
}