diff --git a/images/doc/.gitkeep b/images/doc/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/php/inc_fx_promoteur.php b/php/inc_fx_promoteur.php index 8fcd2c3..3743a6f 100644 --- a/php/inc_fx_promoteur.php +++ b/php/inc_fx_promoteur.php @@ -5987,6 +5987,7 @@ function fxShowBibTool4($str_code, $int_eve_id, $strLangue){ + GetPageWidth() - $pdf->lMargin - $pdf->rMargin; + $tabSize = @getimagesize($strPath); + $fltW = $fltMaxW; + if (is_array($tabSize) && !empty($tabSize[0]) && !empty($tabSize[1])) { + $fltNatW = ($tabSize[0] * 25.4) / 96; + if ($fltNatW > 0 && $fltNatW < $fltMaxW) { + $fltW = $fltNatW; + } + } + if ($pdf->GetY() + 10 > $pdf->GetPageHeight() - $pdf->bMargin) { + $pdf->AddPage(); + } + $pdf->Image($strPath, $pdf->GetX(), $pdf->GetY(), $fltW); + $fltH = 0; + if (is_array($tabSize) && !empty($tabSize[0]) && !empty($tabSize[1])) { + $fltH = $fltW * ($tabSize[1] / $tabSize[0]); + } + $pdf->Ln(max(8, $fltH + 2)); +} + +/** + * MSIN-4471 — Rendu HTML simplifié (doc_html) vers FPDF. + * Tags supportés : h2–h4, p, ul/ol/li, strong/b, em/i, br, img, blockquote, article/div/span. + */ +function fxBibDistPrintWriteHtml($pdf, $strHtml) { + $strHtml = trim((string)$strHtml); + if ($strHtml === '') { + return; + } + if (!class_exists('DOMDocument', false)) { + $pdf->SetFont('Arial', '', 10); + $pdf->MultiCell(0, 5, fxBibDistPrintPdfText(strip_tags($strHtml))); + return; + } + + $dom = new DOMDocument(); + $blnPrev = libxml_use_internal_errors(true); + $dom->loadHTML( + '
' . $strHtml . '
', + LIBXML_HTML_NODEFDTD + ); + libxml_clear_errors(); + libxml_use_internal_errors($blnPrev); + + $root = $dom->getElementById('ms1-bib-dist-root'); + if (!$root) { + $tabNodes = $dom->getElementsByTagName('div'); + foreach ($tabNodes as $el) { + if ($el->getAttribute('id') === 'ms1-bib-dist-root') { + $root = $el; + break; + } + } + } + if (!$root) { + $pdf->SetFont('Arial', '', 10); + $pdf->MultiCell(0, 5, fxBibDistPrintPdfText(strip_tags($strHtml))); + return; + } + + $fnWalk = null; + $fnWalk = function ($node, $intListDepth = 0, $strListType = '') use ($pdf, &$fnWalk) { + if ($node->nodeType === XML_TEXT_NODE) { + $strText = preg_replace('/\s+/u', ' ', $node->nodeValue); + if ($strText !== null && trim($strText) !== '') { + $pdf->Write(5, fxBibDistPrintPdfText($strText)); + } + return; + } + if ($node->nodeType !== XML_ELEMENT_NODE) { + return; + } + + $strTag = strtolower($node->nodeName); + if ($strTag === 'br') { + $pdf->Ln(5); + return; + } + if ($strTag === 'img') { + $strSrc = $node->getAttribute('src'); + $strPath = fxBibDistPrintResolveDocImagePath($strSrc); + if ($strPath !== '') { + $pdf->Ln(2); + fxBibDistPrintWriteImage($pdf, $strPath); + } + return; + } + + $blnBlock = in_array($strTag, array('p', 'h2', 'h3', 'h4', 'li', 'blockquote', 'ul', 'ol'), true); + if ($blnBlock && $pdf->GetX() > $pdf->lMargin + 0.5) { + $pdf->Ln(5); + } + + if (in_array($strTag, array('h2', 'h3', 'h4'), true)) { + $fltSize = ($strTag === 'h2') ? 13 : (($strTag === 'h3') ? 11 : 10); + $pdf->SetFont('Arial', 'B', $fltSize); + if ($pdf->GetY() > $pdf->tMargin + 2) { + $pdf->Ln(2); + } + } elseif (in_array($strTag, array('strong', 'b'), true)) { + $pdf->SetFont('Arial', 'B', 10); + } elseif (in_array($strTag, array('em', 'i'), true)) { + $pdf->SetFont('Arial', 'I', 10); + } elseif ($strTag === 'blockquote') { + $pdf->SetFont('Arial', 'I', 10); + $pdf->SetLeftMargin($pdf->lMargin + 6); + $pdf->SetX($pdf->lMargin); + } elseif ($strTag === 'li') { + $pdf->SetFont('Arial', '', 10); + $strBullet = '- '; + // Compteur ol : index parmi les frères
  • + if ($strListType === 'ol' && $node->parentNode) { + $intIdx = 1; + foreach ($node->parentNode->childNodes as $sib) { + if ($sib === $node) { + break; + } + if ($sib->nodeType === XML_ELEMENT_NODE && strtolower($sib->nodeName) === 'li') { + $intIdx++; + } + } + $strBullet = $intIdx . '. '; + } + $pdf->SetX($pdf->lMargin + (6 * max(0, $intListDepth))); + $pdf->Write(5, fxBibDistPrintPdfText($strBullet)); + } elseif (in_array($strTag, array('p', 'div', 'span', 'article'), true)) { + $pdf->SetFont('Arial', '', 10); + } + + $strChildList = $strListType; + $intChildDepth = $intListDepth; + if ($strTag === 'ul') { + $strChildList = 'ul'; + $intChildDepth = $intListDepth + 1; + } elseif ($strTag === 'ol') { + $strChildList = 'ol'; + $intChildDepth = $intListDepth + 1; + } + + foreach ($node->childNodes as $child) { + $fnWalk($child, $intChildDepth, $strChildList); + } + + if ($strTag === 'blockquote') { + $pdf->SetLeftMargin($pdf->lMargin - 6); + } + if ($blnBlock) { + $pdf->Ln(5); + $pdf->SetX($pdf->lMargin); + } + if (in_array($strTag, array('h2', 'h3', 'h4', 'strong', 'b', 'em', 'i', 'blockquote', 'li', 'p'), true)) { + $pdf->SetFont('Arial', '', 10); + } + }; + + foreach ($root->childNodes as $child) { + $fnWalk($child, 0, ''); + } +} + +/** MSIN-4471 — Dessine le QR résultats événement (kc.php?t=eve) centré. */ +function fxBibDistPrintDrawEventQr($pdf, $int_eve_id) { + $int_eve_id = (int)$int_eve_id; + if ($int_eve_id <= 0 || !function_exists('fxKcEveResultatsQrPngBinary')) { + return; + } + $binPng = fxKcEveResultatsQrPngBinary($int_eve_id, null, 12, true); + if ($binPng === false || $binPng === '') { + return; + } + $strTmp = tempnam(sys_get_temp_dir(), 'bibqr_'); + if ($strTmp === false) { + return; + } + @unlink($strTmp); + $strTmpPng = $strTmp . '.png'; + if (@file_put_contents($strTmpPng, $binPng) === false) { + return; + } + $fltSize = 70; + $fltX = ($pdf->GetPageWidth() - $fltSize) / 2; + $pdf->Ln(4); + if ($pdf->GetY() + $fltSize > $pdf->GetPageHeight() - $pdf->bMargin) { + $pdf->AddPage(); + } + $pdf->Image($strTmpPng, $fltX, $pdf->GetY(), $fltSize, $fltSize); + $pdf->Ln($fltSize + 4); + if (function_exists('fxKcEveResultatsQrUrl')) { + $pdf->SetFont('Arial', '', 8); + $pdf->MultiCell(0, 4, fxBibDistPrintPdfText(fxKcEveResultatsQrUrl($int_eve_id)), 0, 'C'); + } + @unlink($strTmpPng); +} + +/** + * MSIN-4471 — 3 pages d'intro (doc_page) avant les listes d'épreuves. + */ +function fxBibOutputDistPrintIntroPages($pdf, $int_eve_id, $strLangue = 'fr') { + // Aligner fxDocLangue() sur la langue du PDF. + $GLOBALS['strLangue'] = ($strLangue === 'en') ? 'en' : 'fr'; + + if (!function_exists('fxDocSchemaReady') || !fxDocSchemaReady()) { + return; + } + + $pdf->blnIntroMode = true; + $pdf->eprTitle = ''; + $strDefaultDocTitle = fxBibTexte('bib_v4_dist_print_title', 0); + + foreach (fxBibDistPrintIntroPageClefs() as $strClef) { + $row = function_exists('fxDocGetPageRow') ? fxDocGetPageRow($strClef, 0) : array(); + $strTitre = trim((string)($row['doc_titre'] ?? '')); + $strHtml = trim((string)($row['doc_html'] ?? '')); + if ($strTitre === '' && $strHtml === '') { + continue; + } + $pdf->documentTitle = ($strTitre !== '') ? $strTitre : $strClef; + $pdf->AddPage(); + fxBibDistPrintWriteHtml($pdf, $strHtml); + if ($strClef === 'bib_dist_print_qr') { + fxBibDistPrintDrawEventQr($pdf, $int_eve_id); + } + } + + $pdf->blnIntroMode = false; + $pdf->documentTitle = $strDefaultDocTitle; + $pdf->eprTitle = ''; +} + /** MSIN-4433 — Génère et envoie le PDF (format lettre). */ function fxBibOutputDistPrintPdf($int_eve_id, $strLangue = 'fr', $strPrintedBy = '') { global $objDatabase; @@ -9548,14 +9824,16 @@ function fxBibOutputDistPrintPdf($int_eve_id, $strLangue = 'fr', $strPrintedBy = public $eventTitle = ''; public $documentTitle = ''; public $eprTitle = ''; + /** MSIN-4471 — Pages intro : pas de sous-titre épreuve. */ + public $blnIntroMode = false; function Header() { $this->SetFont('Arial', 'B', 14); $this->Cell(0, 8, fxBibDistPrintPdfText($this->eventTitle), 0, 1, 'C'); - $this->SetFont('Arial', '', 10); + $this->SetFont('Arial', $this->blnIntroMode ? 'B' : '', $this->blnIntroMode ? 11 : 10); $this->Cell(0, 6, fxBibDistPrintPdfText($this->documentTitle), 0, 1, 'C'); $this->Ln(2); - if ($this->eprTitle !== '') { + if (!$this->blnIntroMode && $this->eprTitle !== '') { $this->SetFont('Arial', 'B', 11); $this->Cell(0, 7, fxBibDistPrintPdfText($this->eprTitle), 0, 1, 'L'); $this->Ln(1); @@ -9582,6 +9860,9 @@ function fxBibOutputDistPrintPdf($int_eve_id, $strLangue = 'fr', $strPrintedBy = $pdf->SetAutoPageBreak(true, 14); $pdf->AliasNbPages(); + // MSIN-4471 — Pages d'explication (doc) avant les tableaux d'épreuves. + fxBibOutputDistPrintIntroPages($pdf, $int_eve_id, $strLangue); + $fltPageW = $pdf->GetPageWidth() - 20; $fltFixedW = 18 + 42 + 42 + 12; $blnHasPrintedEpreuve = false; @@ -9664,7 +9945,7 @@ function fxBibOutputDistPrintPdf($int_eve_id, $strLangue = 'fr', $strPrintedBy = $pdf->Ln(5); } - if (!$blnHasPrintedEpreuve) { + if (!$blnHasPrintedEpreuve && $pdf->PageNo() < 1) { $pdf->AddPage(); } @@ -9694,6 +9975,15 @@ function renderBibDistPrintPanelShell($int_eve_id, $strLangue = 'fr') { + @@ -9738,6 +10028,15 @@ function renderBibDistPrintPanel($int_eve_id, $strLangue = 'fr', $tabPanel = nul + diff --git a/sql/MSIN-4471-bib-dist-print-doc.sql b/sql/MSIN-4471-bib-dist-print-doc.sql new file mode 100644 index 0000000..761870b --- /dev/null +++ b/sql/MSIN-4471-bib-dist-print-doc.sql @@ -0,0 +1,44 @@ +-- MSIN-4471 — Pages intro PDF distribution des dossards +-- Prérequis : tables doc_mod / doc_page / doc_anchor (MSIN-doc-module.sql) +-- Idempotent : DELETE puis INSERT du module bib_dist_print +-- Images optionnelles : déposer sous images/doc/ (ex. /images/doc/exemple.png) + +SET NAMES utf8mb4; + +DELETE FROM doc_page WHERE doc_mod_clef = 'bib_dist_print'; +DELETE FROM doc_anchor WHERE doc_anchor_clef = 'bib_v4_dist_print_doc'; +DELETE FROM doc_mod WHERE doc_mod_clef = 'bib_dist_print'; + +INSERT INTO `doc_mod` (`doc_mod_clef`, `doc_mod_prg`, `doc_mod_titre`, `doc_mod_actif`, `doc_mod_tri`, `doc_mod_creation`) +VALUES ('bib_dist_print', 'compte.php', 'PDF distribution des dossards', 1, 15, NOW()); + +INSERT INTO `doc_anchor` (`doc_anchor_clef`, `doc_mod_clef`, `doc_prg`, `doc_actif`, `doc_creation`) +VALUES ('bib_v4_dist_print_doc', 'bib_dist_print', 'compte.php', 1, NOW()); + +-- --------------------------------------------------------------------------- +-- FR +-- --------------------------------------------------------------------------- +INSERT INTO `doc_page` (`doc_mod_clef`, `doc_page_clef`, `doc_langue`, `doc_titre`, `doc_sous_titre`, `doc_html`, `doc_prg`, `doc_tri`, `doc_actif`, `doc_creation`) VALUES +('bib_dist_print', 'bib_dist_print_reco', 'fr', 'Recommandations — distribution des dossards', 'À lire avant de commencer', +'

    Texte provisoire (MSIN-4471) — Remplacez ce contenu via l''éditeur de documentation.

    Règles de base

    1. Vérifiez l''identité du participant avant de remettre le dossard.
    2. Cochez ou rayez le numéro sur la liste imprimée après chaque remise.
    3. Gardez les dossards non remis dans un bac séparé clairement étiqueté.

    Organisation de la table

    Astuce images : déposez vos fichiers dans images/doc/ puis utilisez <img src="/images/doc/nom-fichier.png">.
    ', +'compte.php', 10, 1, NOW()), +('bib_dist_print', 'bib_dist_print_table', 'fr', 'Affiche table — participants', 'À placer sur la table de distribution', +'

    Texte provisoire — Page destinée à être lue par les participants à la table.

    À faire avant de vous présenter

    À la table

    1. Donnez votre nom de famille clairement.
    2. Vérifiez le numéro de dossard avant de quitter la table.
    3. Épinglez le dossard bien visible à l''avant.

    Vous pouvez ajouter une image d''exemple ici, par ex. :

    Exemple d''affichage

    ', +'compte.php', 20, 1, NOW()), +('bib_dist_print', 'bib_dist_print_qr', 'fr', 'Code QR de l''événement', 'À afficher / découper pour l''événement', +'

    Texte provisoire — Le code QR ci-dessous pointe vers les résultats (ou le lien configuré) de cet événement.

    Vous pouvez le photocopier, le découper et le coller partout sur le site de l''événement.

    Comment l''utiliser

    Le QR est généré automatiquement à l''impression — inutile de l''insérer en image dans ce texte.

    ', +'compte.php', 30, 1, NOW()); + +-- --------------------------------------------------------------------------- +-- EN +-- --------------------------------------------------------------------------- +INSERT INTO `doc_page` (`doc_mod_clef`, `doc_page_clef`, `doc_langue`, `doc_titre`, `doc_sous_titre`, `doc_html`, `doc_prg`, `doc_tri`, `doc_actif`, `doc_creation`) VALUES +('bib_dist_print', 'bib_dist_print_reco', 'en', 'Recommendations — bib distribution', 'Read before you start', +'

    Placeholder text (MSIN-4471) — Replace this content in the documentation editor.

    Basic rules

    1. Verify the participant''s identity before handing out the bib.
    2. Check or cross off the number on the printed list after each handout.
    3. Keep unclaimed bibs in a clearly labeled separate bin.

    Table setup

    Image tip: place files under images/doc/ then use <img src="/images/doc/filename.png">.
    ', +'compte.php', 10, 1, NOW()), +('bib_dist_print', 'bib_dist_print_table', 'en', 'Table sign — participants', 'Place on the distribution table', +'

    Placeholder — Page meant to be read by participants at the table.

    Before you come up

    At the table

    1. Say your last name clearly.
    2. Check the bib number before leaving the table.
    3. Pin the bib clearly on the front.

    You can add a sample image here, e.g.:

    Sample display

    ', +'compte.php', 20, 1, NOW()), +('bib_dist_print', 'bib_dist_print_qr', 'en', 'Event QR code', 'Display / cut out for the event', +'

    Placeholder — The QR code below points to this event''s results (or configured link).

    You can photocopy, cut and post it around the event site.

    How to use it

    The QR is generated automatically when printing — no need to embed it as an image in this text.

    ', +'compte.php', 30, 1, NOW());