- Optimisation des listes de passages (user/admin) - Amélioration du flux de création avec validation temps réel - Amélioration du flux de consultation avec export multi-formats - Amélioration du flux de modification avec suivi des changements - Ajout de la génération PDF pour les reçus - Migration de la structure des uploads - Implémentation de la file d'attente d'emails - Ajout des permissions de suppression de passages - Corrections de bugs et optimisations performances 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
178 lines
6.2 KiB
PHP
178 lines
6.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
/**
|
|
* Générateur de PDF simple avec support d'images
|
|
* Génère des PDF légers avec logo
|
|
*/
|
|
class SimplePDF {
|
|
private string $content = '';
|
|
private array $objects = [];
|
|
private int $objectCount = 0;
|
|
private array $xref = [];
|
|
private float $pageWidth = 595.0; // A4 width in points
|
|
private float $pageHeight = 842.0; // A4 height in points
|
|
private float $margin = 50.0;
|
|
private float $currentY = 0;
|
|
private int $fontObject = 0;
|
|
private int $pageObject = 0;
|
|
|
|
public function __construct() {
|
|
$this->currentY = $this->pageHeight - $this->margin;
|
|
}
|
|
|
|
/**
|
|
* Ajoute du texte au PDF
|
|
*/
|
|
public function addText(string $text, float $x, float $y, int $fontSize = 12): void {
|
|
$this->content .= "BT\n";
|
|
$this->content .= "/F1 $fontSize Tf\n";
|
|
$this->content .= "$x $y Td\n";
|
|
$this->content .= "(" . $this->escapeString($text) . ") Tj\n";
|
|
$this->content .= "ET\n";
|
|
}
|
|
|
|
/**
|
|
* Ajoute une ligne de texte avec positionnement automatique
|
|
*/
|
|
public function addLine(string $text, int $fontSize = 11, string $align = 'left'): void {
|
|
$x = $this->margin;
|
|
|
|
if ($align === 'center') {
|
|
// Estimation approximative de la largeur du texte
|
|
$textWidth = strlen($text) * $fontSize * 0.5;
|
|
$x = ($this->pageWidth - $textWidth) / 2;
|
|
} elseif ($align === 'right') {
|
|
$textWidth = strlen($text) * $fontSize * 0.5;
|
|
$x = $this->pageWidth - $this->margin - $textWidth;
|
|
}
|
|
|
|
$this->addText($text, $x, $this->currentY, $fontSize);
|
|
$this->currentY -= ($fontSize + 8); // Line height
|
|
}
|
|
|
|
/**
|
|
* Ajoute un espace vertical
|
|
*/
|
|
public function addSpace(float $space = 20): void {
|
|
$this->currentY -= $space;
|
|
}
|
|
|
|
/**
|
|
* Ajoute une ligne horizontale
|
|
*/
|
|
public function addHorizontalLine(): void {
|
|
$y = $this->currentY;
|
|
$this->content .= "q\n"; // Save state
|
|
$this->content .= "0.5 w\n"; // Line width
|
|
$this->content .= $this->margin . " $y m\n"; // Move to start
|
|
$this->content .= ($this->pageWidth - $this->margin) . " $y l\n"; // Line to end
|
|
$this->content .= "S\n"; // Stroke
|
|
$this->content .= "Q\n"; // Restore state
|
|
$this->currentY -= 10;
|
|
}
|
|
|
|
/**
|
|
* Ajoute un rectangle (pour encadrer)
|
|
*/
|
|
public function addRectangle(float $x, float $y, float $width, float $height, bool $fill = false): void {
|
|
$this->content .= "q\n";
|
|
$this->content .= "0.8 w\n"; // Line width
|
|
$this->content .= "$x $y $width $height re\n"; // Rectangle
|
|
$this->content .= $fill ? "f\n" : "S\n"; // Fill or Stroke
|
|
$this->content .= "Q\n";
|
|
}
|
|
|
|
/**
|
|
* Échappe les caractères spéciaux pour le PDF
|
|
*/
|
|
private function escapeString(string $str): string {
|
|
// Échapper les caractères spéciaux PDF
|
|
$str = str_replace('\\', '\\\\', $str);
|
|
$str = str_replace('(', '\\(', $str);
|
|
$str = str_replace(')', '\\)', $str);
|
|
|
|
// Convertir les caractères accentués
|
|
$accents = [
|
|
'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A',
|
|
'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a',
|
|
'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E',
|
|
'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e',
|
|
'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',
|
|
'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',
|
|
'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O',
|
|
'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o',
|
|
'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U',
|
|
'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u',
|
|
'Ñ' => 'N', 'ñ' => 'n',
|
|
'Ç' => 'C', 'ç' => 'c',
|
|
'€' => 'EUR',
|
|
'Œ' => 'OE', 'œ' => 'oe',
|
|
'Æ' => 'AE', 'æ' => 'ae'
|
|
];
|
|
|
|
$str = strtr($str, $accents);
|
|
|
|
// Supprimer tout caractère non-ASCII restant
|
|
$str = preg_replace('/[^\x20-\x7E]/', '', $str);
|
|
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* Génère le PDF final
|
|
*/
|
|
public function generate(): string {
|
|
// Début du PDF
|
|
$pdf = "%PDF-1.4\n";
|
|
$pdf .= "%âãÏÓ\n"; // Binary marker
|
|
|
|
// Object 1 - Catalog
|
|
$this->objects[1] = "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n";
|
|
|
|
// Object 2 - Pages
|
|
$this->objects[2] = "2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n";
|
|
|
|
// Object 3 - Page
|
|
$this->objects[3] = "3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 " .
|
|
$this->pageWidth . " " . $this->pageHeight .
|
|
"] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>\nendobj\n";
|
|
|
|
// Object 4 - Font (Helvetica)
|
|
$this->objects[4] = "4 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>\nendobj\n";
|
|
|
|
// Object 5 - Content stream
|
|
$contentLength = strlen($this->content);
|
|
$this->objects[5] = "5 0 obj\n<< /Length $contentLength >>\nstream\n" .
|
|
$this->content . "\nendstream\nendobj\n";
|
|
|
|
// Construction du PDF final
|
|
$offset = strlen($pdf);
|
|
foreach ($this->objects as $obj) {
|
|
$this->xref[] = $offset;
|
|
$pdf .= $obj;
|
|
$offset += strlen($obj);
|
|
}
|
|
|
|
// Table xref
|
|
$xrefStart = $offset;
|
|
$pdf .= "xref\n";
|
|
$pdf .= "0 " . (count($this->objects) + 1) . "\n";
|
|
$pdf .= "0000000000 65535 f \n";
|
|
foreach ($this->xref as $off) {
|
|
$pdf .= sprintf("%010d 00000 n \n", $off);
|
|
}
|
|
|
|
// Trailer
|
|
$pdf .= "trailer\n";
|
|
$pdf .= "<< /Size " . (count($this->objects) + 1) . " /Root 1 0 R >>\n";
|
|
$pdf .= "startxref\n";
|
|
$pdf .= "$xrefStart\n";
|
|
$pdf .= "%%EOF\n";
|
|
|
|
return $pdf;
|
|
}
|
|
} |