Initial Ladill Give extraction — online giving pages at give.ladill.com.
Donation checkout via Ladill Pay (9% fee), church/giving QR pages, SSO, and platform scan forwarding. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Qr;
|
||||
|
||||
class QrPdfExporter
|
||||
{
|
||||
public function fromPng(string $pngBinary, string $title): string
|
||||
{
|
||||
if (! extension_loaded('gd')) {
|
||||
throw new \RuntimeException('PDF export requires the GD extension.');
|
||||
}
|
||||
|
||||
$image = imagecreatefromstring($pngBinary);
|
||||
if ($image === false) {
|
||||
throw new \RuntimeException('Could not read QR image for PDF export.');
|
||||
}
|
||||
|
||||
$width = imagesx($image);
|
||||
$height = imagesy($image);
|
||||
$canvas = imagecreatetruecolor($width, $height);
|
||||
$white = imagecolorallocate($canvas, 255, 255, 255);
|
||||
imagefilledrectangle($canvas, 0, 0, $width, $height, $white);
|
||||
imagecopy($canvas, $image, 0, 0, 0, 0, $width, $height);
|
||||
imagedestroy($image);
|
||||
|
||||
ob_start();
|
||||
imagejpeg($canvas, null, 95);
|
||||
$jpeg = (string) ob_get_clean();
|
||||
imagedestroy($canvas);
|
||||
|
||||
return $this->buildPdf($jpeg, $title, $width, $height);
|
||||
}
|
||||
|
||||
private function buildPdf(string $jpeg, string $title, int $imgW, int $imgH): string
|
||||
{
|
||||
$pageW = 595.28;
|
||||
$pageH = 841.89;
|
||||
$margin = 48;
|
||||
$maxQr = min($pageW - ($margin * 2), 320);
|
||||
$scale = min($maxQr / max(1, $imgW), $maxQr / max(1, $imgH));
|
||||
$drawW = $imgW * $scale;
|
||||
$drawH = $imgH * $scale;
|
||||
$x = ($pageW - $drawW) / 2;
|
||||
$y = 120;
|
||||
$safeTitle = $this->pdfEscape(substr($title, 0, 80));
|
||||
|
||||
$objects = [];
|
||||
$objects[] = "1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj\n";
|
||||
$objects[] = "2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj\n";
|
||||
$objects[] = sprintf(
|
||||
"3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 %.2F %.2F] /Resources << /Font << /F1 4 0 R >> /XObject << /Im1 5 0 R >> >> /Contents 6 0 R >> endobj\n",
|
||||
$pageW,
|
||||
$pageH,
|
||||
);
|
||||
$objects[] = "4 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >> endobj\n";
|
||||
$objects[] = sprintf(
|
||||
"5 0 obj << /Type /XObject /Subtype /Image /Width %d /Height %d /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length %d >> stream\n%s\nendstream\nendobj\n",
|
||||
$imgW,
|
||||
$imgH,
|
||||
strlen($jpeg),
|
||||
$jpeg,
|
||||
);
|
||||
|
||||
$content = "BT /F1 16 Tf 48 " . ($pageH - 72) . " Td ({$safeTitle}) Tj ET\n";
|
||||
$content .= sprintf("q %.4F 0 0 %.4F %.2F %.2F cm /Im1 Do Q\n", $drawW, $drawH, $x, $pageH - $y - $drawH);
|
||||
$content .= "BT /F1 10 Tf 48 48 Td (Generated by Ladill QR Codes) Tj ET\n";
|
||||
$objects[] = sprintf("6 0 obj << /Length %d >> stream\n%s\nendstream\nendobj\n", strlen($content), $content);
|
||||
|
||||
$pdf = "%PDF-1.4\n";
|
||||
$offsets = [0];
|
||||
|
||||
foreach ($objects as $object) {
|
||||
$offsets[] = strlen($pdf);
|
||||
$pdf .= $object;
|
||||
}
|
||||
|
||||
$xrefPos = strlen($pdf);
|
||||
$pdf .= "xref\n0 " . count($offsets) . "\n";
|
||||
$pdf .= "0000000000 65535 f \n";
|
||||
for ($i = 1; $i < count($offsets); $i++) {
|
||||
$pdf .= sprintf("%010d 00000 n \n", $offsets[$i]);
|
||||
}
|
||||
$pdf .= "trailer << /Size " . count($offsets) . " /Root 1 0 R >>\n";
|
||||
$pdf .= "startxref\n{$xrefPos}\n%%EOF";
|
||||
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
private function pdfEscape(string $text): string
|
||||
{
|
||||
return str_replace(['\\', '(', ')'], ['\\\\', '\\(', '\\)'], $text);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user