616 lines
22 KiB
PHP
Executable File
616 lines
22 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Models\Configuration;
|
|
use App\Models\LateSlipLog;
|
|
use App\Models\User;
|
|
use Carbon\Carbon;
|
|
use Dompdf\Dompdf;
|
|
use Dompdf\Options;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Response;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Session;
|
|
use Mike42\Escpos\Printer;
|
|
use Mike42\Escpos\PrintConnectors\NetworkPrintConnector;
|
|
use Mike42\Escpos\PrintConnectors\UsbPrintConnector;
|
|
use Mike42\Escpos\PrintConnectors\WindowsPrintConnector;
|
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
|
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
|
|
|
|
class SlipPrinterController extends BaseApiController
|
|
{
|
|
protected Configuration $config;
|
|
protected LateSlipLog $log;
|
|
protected User $user;
|
|
protected string $schoolYear;
|
|
protected string $semester;
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
$this->config = model(Configuration::class);
|
|
$this->log = model(LateSlipLog::class);
|
|
$this->user = model(User::class);
|
|
$this->schoolYear = (string) ($this->config->getConfig('school_year') ?? '');
|
|
$this->semester = (string) ($this->config->getConfig('semester') ?? '');
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/slips/print
|
|
* Generate and return a PDF late slip
|
|
*/
|
|
public function print(): SymfonyResponse|JsonResponse
|
|
{
|
|
$data = $this->payloadData();
|
|
|
|
$slipData = [
|
|
'school_year' => trim((string) ($data['school_year'] ?? $this->schoolYear ?: '')),
|
|
'student_name' => trim((string) ($data['student_name'] ?? '')),
|
|
'date' => trim((string) ($data['date'] ?? date('m/d/Y'))),
|
|
'time_in' => trim((string) ($data['time_in'] ?? date('h:i A'))),
|
|
'grade' => trim((string) ($data['grade'] ?? '')),
|
|
'reason' => trim((string) ($data['reason'] ?? '')),
|
|
'admin_name' => trim((string) ($data['admin_name'] ?? '')),
|
|
];
|
|
|
|
// Always prefer current logged-in user's full name from DB
|
|
$adminFromDb = $this->currentAdminName();
|
|
if ($adminFromDb !== '') {
|
|
$slipData['admin_name'] = $adminFromDb;
|
|
}
|
|
|
|
// Validate minimal fields
|
|
if ($slipData['student_name'] === '') {
|
|
return $this->error('Student name is required.', SymfonyResponse::HTTP_UNPROCESSABLE_ENTITY);
|
|
}
|
|
|
|
try {
|
|
// Persist a log (best-effort) before generating PDF
|
|
try {
|
|
$printedBy = $this->getCurrentUserId();
|
|
$logData = [
|
|
'school_year' => $slipData['school_year'],
|
|
'semester' => $this->semester,
|
|
'student_name' => $slipData['student_name'],
|
|
'slip_date' => $this->toDbDate($slipData['date']),
|
|
'time_in' => $this->toDbTime($slipData['time_in']),
|
|
'grade' => $slipData['grade'],
|
|
'reason' => $slipData['reason'],
|
|
'admin_name' => $slipData['admin_name'],
|
|
];
|
|
$this->log->logSlip($logData, $printedBy);
|
|
} catch (\Throwable $e) {
|
|
Log::error('Late slip log insert failed: ' . $e->getMessage());
|
|
}
|
|
|
|
// Generate and return PDF
|
|
return $this->renderSlipPdfResponse($slipData);
|
|
} catch (\Throwable $e) {
|
|
Log::error('Late slip PDF generation failed: ' . $e->getMessage());
|
|
return $this->error('Printing failed.', SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR, [
|
|
'details' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET/POST /api/v1/slips/preview
|
|
* Show preview of late slips (GET returns list, POST returns JSON preview)
|
|
*/
|
|
public function preview(): JsonResponse
|
|
{
|
|
try {
|
|
// Resolve current school year
|
|
$currentYear = trim((string) ($this->schoolYear ?? ''));
|
|
if ($currentYear === '') {
|
|
$latest = $this->log->newQuery()
|
|
->select('school_year')
|
|
->orderBy('id', 'DESC')
|
|
->first();
|
|
$currentYear = $latest['school_year'] ?? (date('Y') . '-' . (date('Y') + 1));
|
|
}
|
|
|
|
$data = $this->payloadData();
|
|
$selectedYear = trim((string) ($data['school_year'] ?? $currentYear));
|
|
$selectedSemester = strtolower(trim((string) ($data['semester'] ?? '')));
|
|
|
|
$previewData = [
|
|
'school_year' => $selectedYear,
|
|
'student_name' => trim((string) ($data['student_name'] ?? '')),
|
|
'date' => trim((string) ($data['date'] ?? date('m/d/Y'))),
|
|
'time_in' => trim((string) ($data['time_in'] ?? date('h:i A'))),
|
|
'grade' => trim((string) ($data['grade'] ?? '')),
|
|
'reason' => trim((string) ($data['reason'] ?? '')),
|
|
'admin_name' => trim((string) ($data['admin_name'] ?? '')),
|
|
];
|
|
|
|
// Prefer logged-in admin name
|
|
$adminFromDb = $this->currentAdminName();
|
|
if ($adminFromDb !== '') {
|
|
$previewData['admin_name'] = $adminFromDb;
|
|
}
|
|
|
|
// Handle GET (return list of recent slips)
|
|
if (strtolower($this->laravelRequest->method()) === 'get') {
|
|
$builder = $this->log->newQuery();
|
|
|
|
// Filter by school year if not empty
|
|
if ($selectedYear !== '') {
|
|
$builder->where('school_year', $selectedYear);
|
|
}
|
|
|
|
// Normalize and apply semester filter
|
|
$hasFilter = false;
|
|
if ($selectedSemester !== '') {
|
|
$hasFilter = true;
|
|
if ($selectedSemester === 'fall') {
|
|
$builder->where(function ($q) {
|
|
$q->where('semester', 'fall')
|
|
->orWhere('semester', '1')
|
|
->orWhere('semester', 1);
|
|
});
|
|
} elseif ($selectedSemester === 'spring') {
|
|
$builder->where(function ($q) {
|
|
$q->where('semester', 'spring')
|
|
->orWhere('semester', '2')
|
|
->orWhere('semester', 2);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Execute query
|
|
$logs = $builder->orderBy('id', 'DESC')->limit(50)->get()->toArray();
|
|
|
|
// Only use fallback when no filters are selected
|
|
if (empty($logs) && !$hasFilter && $selectedYear === '') {
|
|
$logs = $this->log->newQuery()->orderBy('id', 'DESC')->limit(50)->get()->toArray();
|
|
}
|
|
|
|
// Format rows
|
|
$rows = [];
|
|
foreach ($logs as $r) {
|
|
$dispDate = '';
|
|
if (!empty($r['slip_date'])) {
|
|
$ts = strtotime($r['slip_date']);
|
|
$dispDate = $ts ? date('m/d/Y', $ts) : '';
|
|
}
|
|
|
|
$dispTime = '';
|
|
if (!empty($r['time_in'])) {
|
|
$t = strtotime($r['time_in']) ?: strtotime('today ' . $r['time_in']);
|
|
$dispTime = $t ? date('h:i A', $t) : '';
|
|
}
|
|
|
|
$rows[] = [
|
|
'id' => (int) ($r['id'] ?? 0),
|
|
'school_year' => (string) ($r['school_year'] ?? ''),
|
|
'semester' => (string) ($r['semester'] ?? ''),
|
|
'student_name' => (string) ($r['student_name'] ?? ''),
|
|
'date' => $dispDate,
|
|
'time_in' => $dispTime,
|
|
'grade' => (string) ($r['grade'] ?? ''),
|
|
'reason' => (string) ($r['reason'] ?? ''),
|
|
'admin_name' => (string) ($r['admin_name'] ?? ''),
|
|
];
|
|
}
|
|
|
|
return $this->success([
|
|
'rows' => $rows,
|
|
'school_year' => $selectedYear,
|
|
'semester' => $selectedSemester,
|
|
], 'Late slip logs retrieved');
|
|
}
|
|
|
|
// POST mode (JSON preview for printer)
|
|
$cfg = $this->printerConfig();
|
|
$lineWidth = (int) ($cfg['chars_per_line'] ?? 48);
|
|
$feedLines = (int) ($cfg['feed_lines'] ?? 3);
|
|
|
|
$header = "Al Rahma School at ISGL\nSchool Year: {$previewData['school_year']}\n\n";
|
|
$body = implode("\n", $this->buildSlipLines($previewData, $lineWidth));
|
|
$footer = str_repeat("\n", $feedLines);
|
|
$full = $header . $body . $footer;
|
|
|
|
return $this->success([
|
|
'ok' => true,
|
|
'text' => $full,
|
|
'width' => $lineWidth,
|
|
], 'Preview generated');
|
|
} catch (\Throwable $e) {
|
|
Log::error('Late slip preview failed: ' . $e->getMessage());
|
|
return $this->error('Preview failed.', SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR, [
|
|
'details' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/slips/reprint/{id}
|
|
* Reprint a late slip from a saved log row
|
|
*/
|
|
public function reprint($id = null): SymfonyResponse|JsonResponse
|
|
{
|
|
$id = (int) ($id ?? 0);
|
|
if ($id <= 0) {
|
|
return $this->error('Invalid slip id.', SymfonyResponse::HTTP_BAD_REQUEST);
|
|
}
|
|
|
|
try {
|
|
$row = $this->log->find($id);
|
|
} catch (\Throwable $e) {
|
|
$row = null;
|
|
}
|
|
|
|
if (!$row) {
|
|
return $this->error('Slip not found.', SymfonyResponse::HTTP_NOT_FOUND);
|
|
}
|
|
|
|
// Map DB row to print payload
|
|
$dateDisplay = '';
|
|
if (!empty($row['slip_date'])) {
|
|
$ts = strtotime($row['slip_date']);
|
|
$dateDisplay = $ts ? date('m/d/Y', $ts) : '';
|
|
}
|
|
if ($dateDisplay === '') {
|
|
$dateDisplay = date('m/d/Y');
|
|
}
|
|
|
|
$timeDisplay = '';
|
|
if (!empty($row['time_in'])) {
|
|
$ts = strtotime($row['time_in']);
|
|
if ($ts === false) {
|
|
$ts = strtotime('today ' . $row['time_in']);
|
|
}
|
|
$timeDisplay = $ts ? date('h:i A', $ts) : '';
|
|
}
|
|
if ($timeDisplay === '') {
|
|
$timeDisplay = date('h:i A');
|
|
}
|
|
|
|
$data = [
|
|
'school_year' => (string) ($row['school_year'] ?? ''),
|
|
'student_name' => (string) ($row['student_name'] ?? ''),
|
|
'date' => $dateDisplay,
|
|
'time_in' => $timeDisplay,
|
|
'grade' => (string) ($row['grade'] ?? ''),
|
|
'reason' => (string) ($row['reason'] ?? ''),
|
|
'admin_name' => (string) ($row['admin_name'] ?? ''),
|
|
];
|
|
|
|
// Always prefer current logged-in user's full name from DB
|
|
$adminFromDb = $this->currentAdminName();
|
|
if ($adminFromDb !== '') {
|
|
$data['admin_name'] = $adminFromDb;
|
|
}
|
|
|
|
try {
|
|
// Log again (best-effort) for audit trail
|
|
try {
|
|
$printedBy = $this->getCurrentUserId();
|
|
$logData = [
|
|
'school_year' => $data['school_year'],
|
|
'semester' => $this->semester,
|
|
'student_name' => $data['student_name'],
|
|
'slip_date' => $this->toDbDate($data['date']),
|
|
'time_in' => $this->toDbTime($data['time_in']),
|
|
'grade' => $data['grade'],
|
|
'reason' => $data['reason'],
|
|
'admin_name' => $data['admin_name'],
|
|
];
|
|
$this->log->logSlip($logData, $printedBy);
|
|
} catch (\Throwable $e) {
|
|
Log::error('Late slip reprint log insert failed: ' . $e->getMessage());
|
|
}
|
|
|
|
// Generate and return PDF
|
|
return $this->renderSlipPdfResponse($data);
|
|
} catch (\Throwable $e) {
|
|
Log::error('Reprint failed: ' . $e->getMessage());
|
|
return $this->error('Reprint failed: ' . $e->getMessage(), SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build monospaced slip lines for receipt printing
|
|
*/
|
|
private function buildSlipLines(array $data, ?int $lineWidth = null): array
|
|
{
|
|
$cfgWidth = (int) ($this->printerConfig()['chars_per_line'] ?? 0);
|
|
$w = $lineWidth ?? ($cfgWidth > 0 ? $cfgWidth : 32);
|
|
|
|
$lines = [];
|
|
$lines[] = $this->fitLine('Student Name: ' . (string) ($data['student_name'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Date: ' . (string) ($data['date'] ?? '') . ' ' . 'Time In: ' . (string) ($data['time_in'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Grade: ' . (string) ($data['grade'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Reason: ' . (string) ($data['reason'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Admin: ' . (string) ($data['admin_name'] ?? ''), $w);
|
|
|
|
return $lines;
|
|
}
|
|
|
|
/**
|
|
* Create and return a configured Printer instance
|
|
*/
|
|
private function makePrinter(): Printer
|
|
{
|
|
$cfg = $this->printerConfig();
|
|
$mode = strtolower((string) ($cfg['mode'] ?? 'network'));
|
|
|
|
if ($mode === 'windows') {
|
|
if (stripos(PHP_OS_FAMILY, 'Windows') !== false) {
|
|
$name = (string) ($cfg['windows_name'] ?? 'POS-80');
|
|
$connector = new WindowsPrintConnector($name);
|
|
return new Printer($connector);
|
|
}
|
|
Log::warning('PRINTER_MODE=windows on non-Windows OS; falling back to network.');
|
|
$mode = 'network';
|
|
}
|
|
|
|
if ($mode === 'usb') {
|
|
if (stripos(PHP_OS_FAMILY, 'Windows') !== false) {
|
|
$name = (string) ($cfg['windows_name'] ?? 'POS-80');
|
|
$connector = new WindowsPrintConnector($name);
|
|
return new Printer($connector);
|
|
}
|
|
$vidStr = (string) ($cfg['usb_vid'] ?? '');
|
|
$pidStr = (string) ($cfg['usb_pid'] ?? '');
|
|
if ($vidStr === '' || $pidStr === '') {
|
|
throw new \RuntimeException('USB mode requires PRINTER_USB_VID and PRINTER_USB_PID');
|
|
}
|
|
$vid = $this->hexToInt($vidStr);
|
|
$pid = $this->hexToInt($pidStr);
|
|
$connector = new UsbPrintConnector($vid, $pid);
|
|
return new Printer($connector);
|
|
}
|
|
|
|
// Default: network
|
|
$host = (string) ($cfg['host'] ?? '192.168.1.100');
|
|
$port = (int) ($cfg['port'] ?? 9100);
|
|
$connector = new NetworkPrintConnector($host, $port);
|
|
return new Printer($connector);
|
|
}
|
|
|
|
/**
|
|
* Ensure any line is exactly $width characters
|
|
*/
|
|
private function fitLine(string $line, int $width): string
|
|
{
|
|
$line = (string) $line;
|
|
if (strlen($line) > $width) {
|
|
return substr($line, 0, $width);
|
|
}
|
|
return $line . str_repeat(' ', $width - strlen($line));
|
|
}
|
|
|
|
/**
|
|
* Convert hex string to int
|
|
*/
|
|
private function hexToInt(string $hexOrDec): int
|
|
{
|
|
$hexOrDec = trim($hexOrDec);
|
|
if ($hexOrDec === '') {
|
|
return 0;
|
|
}
|
|
if (stripos($hexOrDec, '0x') === 0) {
|
|
return intval($hexOrDec, 16);
|
|
}
|
|
return (int) $hexOrDec;
|
|
}
|
|
|
|
/**
|
|
* Get printer configuration from environment
|
|
*/
|
|
private function printerConfig(): array
|
|
{
|
|
$get = function ($keys, $default = null) {
|
|
$keys = is_array($keys) ? $keys : [$keys];
|
|
foreach ($keys as $k) {
|
|
$v = env($k);
|
|
if ($v !== null && $v !== '') {
|
|
return $v;
|
|
}
|
|
}
|
|
return $default;
|
|
};
|
|
|
|
return [
|
|
'mode' => strtolower((string) $get(['printer.mode', 'PRINTER_MODE'], 'network')),
|
|
'host' => (string) $get(['printer.host', 'PRINTER_HOST'], '192.168.1.100'),
|
|
'port' => (int) $get(['printer.port', 'PRINTER_PORT'], 9100),
|
|
'usb_vid' => (string) $get(['printer.usb.vid', 'PRINTER_USB_VID'], ''),
|
|
'usb_pid' => (string) $get(['printer.usb.pid', 'PRINTER_USB_PID'], ''),
|
|
'windows_name' => (string) $get(['printer.windows.name', 'PRINTER_WINDOWS_NAME'], 'POS-80'),
|
|
'chars_per_line' => (int) $get(['printer.chars_per_line', 'PRINTER_CHARS_PER_LINE'], 48),
|
|
'feed_lines' => (int) $get(['printer.feed_lines', 'PRINTER_FEED_LINES'], 3),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Convert user-provided date/time strings into DB formats
|
|
*/
|
|
private function toDbDate(?string $s): ?string
|
|
{
|
|
$s = trim((string) $s);
|
|
if ($s === '') {
|
|
return null;
|
|
}
|
|
$ts = strtotime($s);
|
|
return $ts ? date('Y-m-d', $ts) : null;
|
|
}
|
|
|
|
private function toDbTime(?string $s): ?string
|
|
{
|
|
$s = trim((string) $s);
|
|
if ($s === '') {
|
|
return null;
|
|
}
|
|
$ts = strtotime($s);
|
|
return $ts ? date('H:i:s', $ts) : null;
|
|
}
|
|
|
|
/**
|
|
* Get current admin full name using session user_id
|
|
*/
|
|
private function currentAdminName(): string
|
|
{
|
|
try {
|
|
$uid = $this->getCurrentUserId();
|
|
if ($uid && $uid > 0) {
|
|
$user = $this->user->select('firstname', 'lastname')->find($uid);
|
|
if ($user) {
|
|
$full = trim((string) ($user['firstname'] ?? '') . ' ' . (string) ($user['lastname'] ?? ''));
|
|
if ($full !== '') {
|
|
return $full;
|
|
}
|
|
}
|
|
}
|
|
} catch (\Throwable $e) {
|
|
// ignore and fall back
|
|
}
|
|
|
|
$first = trim((string) (Session::get('firstname') ?? ''));
|
|
$last = trim((string) (Session::get('lastname') ?? ''));
|
|
$full = trim($first . ' ' . $last);
|
|
if ($full !== '') {
|
|
return $full;
|
|
}
|
|
|
|
return (string) (Session::get('user_name') ?? '');
|
|
}
|
|
|
|
/**
|
|
* Render the slip as a PDF and return it as a response
|
|
*/
|
|
private function renderSlipPdfResponse(array $data): SymfonyResponse
|
|
{
|
|
// Determine paper size (mm)
|
|
$paper = strtolower((string) ($this->laravelRequest->input('paper') ?? env('SLIP_PAPER', 'card')));
|
|
|
|
// Estimate dynamic height
|
|
$fontPt = (float) ($this->laravelRequest->input('font_pt') ?? env('SLIP_FONT_PT', 13.0));
|
|
$lineH = (float) ($this->laravelRequest->input('line_h') ?? env('SLIP_LINE_H', 2.0));
|
|
$linesCount = (int) ($this->laravelRequest->input('rows') ?? 8);
|
|
if ($linesCount < 1) {
|
|
$linesCount = 8;
|
|
}
|
|
|
|
$ptPerLine = $fontPt * $lineH;
|
|
$mmPerPt = 25.4 / 72.0;
|
|
$contentMm = $ptPerLine * $linesCount * $mmPerPt;
|
|
$pageMarginsMm = 3.0 * 2;
|
|
$fudgeMm = (float) ($this->laravelRequest->input('fudge_mm') ?? env('SLIP_FUDGE_MM', 12.0));
|
|
$dynHeightMm = $contentMm + $pageMarginsMm + $fudgeMm;
|
|
|
|
switch ($paper) {
|
|
case 'cardp':
|
|
case 'card-portrait':
|
|
$wMm = 53.98;
|
|
$hMm = max($dynHeightMm, 40.0);
|
|
$wChars = 30;
|
|
break;
|
|
case 'receipt80':
|
|
$wMm = 72.0;
|
|
$hMm = 90.0;
|
|
$wChars = 30;
|
|
break;
|
|
case 'receipt58':
|
|
$wMm = 58.0;
|
|
$hMm = 80.0;
|
|
$wChars = 24;
|
|
break;
|
|
case 'card':
|
|
default:
|
|
$wMm = 85.60;
|
|
$hMm = max($dynHeightMm, 40.0);
|
|
$wChars = 40;
|
|
}
|
|
|
|
// Optional manual override
|
|
$hOverride = $this->laravelRequest->input('height_mm') ?? $this->laravelRequest->input('h');
|
|
if (is_numeric($hOverride)) {
|
|
$hMm = max(30.0, min(200.0, (float) $hOverride));
|
|
}
|
|
|
|
// Build lines
|
|
$w = $wChars;
|
|
$lines = [];
|
|
$lines[] = $this->fitLine('Al Rahma School at ISGL ' . ($data['school_year'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Student Name: ' . (string) ($data['student_name'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Date: ' . (string) ($data['date'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Time In: ' . (string) ($data['time_in'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Grade: ' . (string) ($data['grade'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Reason: ' . (string) ($data['reason'] ?? ''), $w);
|
|
$lines[] = $this->fitLine('Admin: ' . (string) ($data['admin_name'] ?? ''), $w);
|
|
|
|
// Generate HTML for PDF
|
|
$html = $this->generateSlipHtml($data, $lines, ['w_mm' => $wMm, 'h_mm' => $hMm]);
|
|
|
|
$options = new Options();
|
|
$options->set('isRemoteEnabled', true);
|
|
$options->set('defaultFont', 'Courier');
|
|
|
|
$dompdf = new Dompdf($options);
|
|
$dompdf->loadHtml($html, 'UTF-8');
|
|
|
|
if ($hMm < $dynHeightMm) {
|
|
$hMm = $dynHeightMm;
|
|
}
|
|
|
|
// Set paper size
|
|
$mmToPt = 72 / 25.4;
|
|
$wPt = $wMm * $mmToPt;
|
|
$hPt = $hMm * $mmToPt;
|
|
$dompdf->setPaper([0, 0, $wPt, $hPt]);
|
|
$dompdf->render();
|
|
|
|
$filename = 'LateSlip_' . preg_replace('/[^A-Za-z0-9]+/', '_', (string) ($data['student_name'] ?? 'slip')) . '_' . date('Ymd_His') . '.pdf';
|
|
|
|
return response($dompdf->output(), 200, [
|
|
'Content-Type' => 'application/pdf',
|
|
'Content-Disposition' => 'inline; filename="' . $filename . '"',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Generate HTML for the slip PDF
|
|
*/
|
|
private function generateSlipHtml(array $data, array $lines, array $page): string
|
|
{
|
|
$wMm = $page['w_mm'];
|
|
$hMm = $page['h_mm'];
|
|
|
|
$html = '<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<style>
|
|
@page {
|
|
margin: 0;
|
|
size: ' . $wMm . 'mm ' . $hMm . 'mm;
|
|
}
|
|
body {
|
|
margin: 3mm;
|
|
font-family: "Courier New", Courier, monospace;
|
|
font-size: 13pt;
|
|
line-height: 2.0;
|
|
}
|
|
.line {
|
|
white-space: pre;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>';
|
|
|
|
foreach ($lines as $line) {
|
|
$html .= '<div class="line">' . htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . '</div>';
|
|
}
|
|
|
|
$html .= '</body>
|
|
</html>';
|
|
|
|
return $html;
|
|
}
|
|
}
|