Files
ms1inscription-v5/php/inc_fx_dossard_print.php

402 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* MSIN-4485 — Impression dossard PDF (1 participant).
*
* Format FINAL : demi-feuille Lettre coupée sur le 11"
* → papier 8.5" large × 5.5" haut (paysage)
* → PDF page = exactement cette taille
* → fond pleine page + n° / nom / QR alignés
*
* Impression : papier personnalisé 8.5 × 5.5, échelle 100 %, sans « ajuster à la page ».
*/
define('MSIN_DOSSARD_PRINT_PERM', 'inscriptions_gestion.dossard_print');
/**
* Taille page = feuille coupée (pouces → mm).
* Lettre 8.5×11 coupée en 2 sur le 11" → 8.5 × 5.5.
*
* @return array{0:float,1:float} [largeur, hauteur]
*/
function fxDossardPrintPageSizeMm()
{
return array(8.5 * 25.4, 5.5 * 25.4);
}
/** Dossier des fonds JPG/PNG (même ratio 8.5:5.5 recommandé). */
function fxDossardPrintTemplatesDir()
{
return dirname(__DIR__) . '/assets/dossard_templates';
}
/**
* Fond : eve_{id}.jpg|png sinon default.jpg|png.
*
* @return string|null
*/
function fxDossardPrintResolveTemplatePath($intEveId)
{
$strDir = fxDossardPrintTemplatesDir();
$intEveId = intval($intEveId);
$arrCandidates = array();
if ($intEveId > 0) {
$arrCandidates[] = $strDir . '/eve_' . $intEveId . '.jpg';
$arrCandidates[] = $strDir . '/eve_' . $intEveId . '.jpeg';
$arrCandidates[] = $strDir . '/eve_' . $intEveId . '.png';
}
$arrCandidates[] = $strDir . '/default.jpg';
$arrCandidates[] = $strDir . '/default.jpeg';
$arrCandidates[] = $strDir . '/default.png';
foreach ($arrCandidates as $strPath) {
if (is_file($strPath)) {
return $strPath;
}
}
return null;
}
/**
* Type FPDF d'après le fichier réel.
*
* @return string JPG|PNG|GIF|''
*/
function fxDossardPrintImageType($strPath)
{
$arrInfo = @getimagesize($strPath);
if (!is_array($arrInfo) || empty($arrInfo[2])) {
return '';
}
if ((int)$arrInfo[2] === IMAGETYPE_JPEG) {
return 'JPG';
}
if ((int)$arrInfo[2] === IMAGETYPE_PNG) {
return 'PNG';
}
if ((int)$arrInfo[2] === IMAGETYPE_GIF) {
return 'GIF';
}
return '';
}
/** Texte FPDF (core fonts = Windows-1252). */
function fxDossardPrintPdfText($str)
{
$str = (string)$str;
if (function_exists('iconv')) {
$strConverted = @iconv('UTF-8', 'Windows-1252//TRANSLIT', $str);
if ($strConverted !== false) {
return $strConverted;
}
}
if (function_exists('mb_convert_encoding')) {
return (string)mb_convert_encoding($str, 'Windows-1252', 'UTF-8');
}
return $str;
}
/** Droit impression (hors kit total). */
function fxDossardPrintCan($intComId, $intEveId)
{
if (!function_exists('fxEveAccesHasPermission')) {
require_once __DIR__ . '/inc_fx_eve_acces.php';
}
return fxEveAccesHasPermission(intval($intComId), intval($intEveId), MSIN_DOSSARD_PRINT_PERM);
}
/**
* @return array|null
*/
function fxDossardPrintLoadParticipant($intParId, $intEveId)
{
global $objDatabase;
$intParId = intval($intParId);
$intEveId = intval($intEveId);
if ($intParId <= 0 || $intEveId <= 0) {
return null;
}
$sql = 'SELECT p.par_id, p.par_prenom, p.par_nom, p.no_bib, p.eve_id, p.epr_id, p.pec_id,
e.pec_id_original, e.pec_actif, e.is_cancelled
FROM resultats_participants p
INNER JOIN resultats_epreuves_commandees e
ON e.pec_id_original = p.pec_id AND e.epr_id = p.epr_id
WHERE p.par_id = ' . $intParId . '
AND p.eve_id = ' . $intEveId . '
LIMIT 1';
$row = $objDatabase->fxGetRow($sql);
return ($row === null || $row === false) ? null : $row;
}
function fxDossardPrintQrUrl($intEveId)
{
if (!function_exists('fxKcEveResultatsQrUrl')) {
require_once __DIR__ . '/inc_fx_eve_extra.php';
}
return fxKcEveResultatsQrUrl(intval($intEveId));
}
/**
* @return string|false chemin PNG temp
*/
function fxDossardPrintBuildQrTempFile($intEveId)
{
require_once __DIR__ . '/../libs/phpqrcode-master/qrlib.php';
$strUrl = fxDossardPrintQrUrl($intEveId);
if ($strUrl === '') {
return false;
}
$strTmp = tempnam(sys_get_temp_dir(), 'ms1dossardqr_');
if ($strTmp === false) {
return false;
}
$strPng = $strTmp . '.png';
@unlink($strTmp);
QRcode::png($strUrl, $strPng, QR_ECLEVEL_M, 6, 1);
if (!is_file($strPng)) {
return false;
}
return $strPng;
}
/**
* Layout calibré pour le fond comic default (8.5" × 5.5").
* Deux boîtes indépendantes en % de page (références user annotées).
*
* @return array<string,mixed>
*/
function fxDossardPrintLayout()
{
$arrSize = fxDossardPrintPageSizeMm();
$fltW = $arrSize[0];
$fltH = $arrSize[1];
// --- Boîte NUMÉRO (grande) — % page, d'après annotation rouge ---
$arrBibPct = array(
'left' => 0.20,
'right' => 0.78,
'top' => 0.36,
'bottom' => 0.66,
);
// --- Boîte PRÉNOM (petite, plus bas, centrée) — PAS collée au n° ---
$arrNamePct = array(
'left' => 0.32,
'right' => 0.68,
'top' => 0.72,
'bottom' => 0.84,
);
$arrBib = fxDossardPrintBoxFromPct($fltW, $fltH, $arrBibPct);
$arrName = fxDossardPrintBoxFromPct($fltW, $fltH, $arrNamePct);
$fltQr = 28.0;
return array(
'page_w' => $fltW,
'page_h' => $fltH,
'bib' => $arrBib,
'name' => $arrName,
'ascent_ratio' => 0.72,
'bib_font_max' => 220,
'name_font_max' => 28,
'qr_size' => $fltQr,
'qr_x' => $fltW - $fltQr - 11.5,
'qr_y' => $fltH - $fltQr - 14.5,
);
}
/**
* Rectangle mm depuis fractions 01 de la page.
*
* @param float $fltPageW
* @param float $fltPageH
* @param array<string,float> $arrPct left|right|top|bottom
* @return array{x:float,y:float,w:float,h:float}
*/
function fxDossardPrintBoxFromPct($fltPageW, $fltPageH, $arrPct)
{
$fltX = $fltPageW * floatval($arrPct['left']);
$fltY = $fltPageH * floatval($arrPct['top']);
$fltW = $fltPageW * (floatval($arrPct['right']) - floatval($arrPct['left']));
$fltH = $fltPageH * (floatval($arrPct['bottom']) - floatval($arrPct['top']));
return array(
'x' => $fltX,
'y' => $fltY,
'w' => $fltW,
'h' => $fltH,
);
}
/**
* Plus grande police Helvetica Bold qui tient dans une boîte (largeur + hauteur).
*
* @param FPDF $pdf
* @param string $strText
* @param float $fltMaxW
* @param float $fltMaxH
* @param int $intFontMax
* @param float $fltAscentRatio
* @return int
*/
function fxDossardPrintFitBibFont($pdf, $strText, $fltMaxW, $fltMaxH, $intFontMax, $fltAscentRatio = 0.72)
{
$strPdf = fxDossardPrintPdfText($strText);
$fltAscentRatio = ($fltAscentRatio > 0) ? $fltAscentRatio : 0.72;
$fltPtPerMm = 72 / 25.4;
$intCapByH = ($fltMaxH > 0)
? (int)floor($fltMaxH * $fltPtPerMm / $fltAscentRatio)
: intval($intFontMax);
$intHi = max(12, min(intval($intFontMax), $intCapByH));
$intLo = 12;
$intBest = $intLo;
while ($intLo <= $intHi) {
$intMid = (int)floor(($intLo + $intHi) / 2);
$pdf->SetFont('Helvetica', 'B', $intMid);
if ($pdf->GetStringWidth($strPdf) <= $fltMaxW) {
$intBest = $intMid;
$intLo = $intMid + 1;
} else {
$intHi = $intMid - 1;
}
}
return $intBest;
}
/**
* Texte centré dans une boîte (Cell = centre H + V FPDF).
*
* @param FPDF $pdf
* @param array $arrBox x,y,w,h
* @param string $strText
* @param int $intFont
*/
function fxDossardPrintDrawCenteredInBox($pdf, $arrBox, $strText, $intFont)
{
$pdf->SetFont('Helvetica', 'B', $intFont);
$pdf->SetXY($arrBox['x'], $arrBox['y']);
$pdf->Cell(
$arrBox['w'],
$arrBox['h'],
fxDossardPrintPdfText($strText),
0,
0,
'C'
);
}
/**
* Génère et envoie le PDF (exit).
*
* @param array $arrRow
*/
function fxDossardPrintOutputPdf($arrRow)
{
$intEveId = intval($arrRow['eve_id'] ?? 0);
$strTemplate = fxDossardPrintResolveTemplatePath($intEveId);
if ($strTemplate === null) {
http_response_code(500);
header('Content-Type: text/plain; charset=utf-8');
echo 'Template dossard introuvable (assets/dossard_templates/default.jpg).';
exit;
}
$strFpdf = dirname(__DIR__) . '/fpdf182/fpdf.php';
if (!is_file($strFpdf)) {
$strDocRoot = $_SERVER['DOCUMENT_ROOT'] ?? '';
$strFpdf = rtrim((string)$strDocRoot, '/\\') . '/fpdf182/fpdf.php';
}
if (!is_file($strFpdf)) {
http_response_code(500);
header('Content-Type: text/plain; charset=utf-8');
echo 'FPDF introuvable (fpdf182/fpdf.php).';
exit;
}
require_once $strFpdf;
$arrLayout = fxDossardPrintLayout();
$strBib = trim((string)($arrRow['no_bib'] ?? ''));
if ($strBib === '' || $strBib === '0') {
$strBib = '-';
}
// MSIN-4485 — prénom seul, centré dans sa boîte (%).
$strName = trim(function_exists('fxUnescape') ? fxUnescape($arrRow['par_prenom'] ?? '') : (string)($arrRow['par_prenom'] ?? ''));
$strQrFile = fxDossardPrintBuildQrTempFile($intEveId);
$strTplType = fxDossardPrintImageType($strTemplate);
$pdf = new FPDF('L', 'mm', array($arrLayout['page_w'], $arrLayout['page_h']));
$pdf->SetAutoPageBreak(false);
$pdf->SetMargins(0, 0, 0);
$pdf->AddPage();
$pdf->Image(
$strTemplate,
0,
0,
$arrLayout['page_w'],
$arrLayout['page_h'],
$strTplType
);
$pdf->SetTextColor(0, 0, 0);
// 1) Numéro → grande boîte, centré, police max
$arrBibBox = $arrLayout['bib'];
$intBibFont = fxDossardPrintFitBibFont(
$pdf,
$strBib,
$arrBibBox['w'] * 0.98,
$arrBibBox['h'] * 0.92,
$arrLayout['bib_font_max'],
$arrLayout['ascent_ratio']
);
fxDossardPrintDrawCenteredInBox($pdf, $arrBibBox, $strBib, $intBibFont);
// 2) Prénom → petite boîte, centré, police max
if ($strName !== '') {
$arrNameBox = $arrLayout['name'];
$intNameFont = fxDossardPrintFitBibFont(
$pdf,
$strName,
$arrNameBox['w'] * 0.95,
$arrNameBox['h'] * 0.90,
$arrLayout['name_font_max'],
$arrLayout['ascent_ratio']
);
fxDossardPrintDrawCenteredInBox($pdf, $arrNameBox, $strName, $intNameFont);
}
if ($strQrFile !== false) {
$pdf->Image(
$strQrFile,
$arrLayout['qr_x'],
$arrLayout['qr_y'],
$arrLayout['qr_size'],
$arrLayout['qr_size'],
'PNG'
);
@unlink($strQrFile);
}
$strSafeBib = preg_replace('/[^a-zA-Z0-9_-]+/', '-', $strBib);
$strFilename = 'dossard-' . $strSafeBib . '-par' . intval($arrRow['par_id']) . '.pdf';
$pdf->Output('I', $strFilename);
exit;
}