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, ]; } }