255 lines
9.3 KiB
PHP
255 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,
|
|
];
|
|
}
|
|
}
|