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