Import initial de Vivier depuis l'hebergement OVH

This commit is contained in:
Yann PILLA committed 2026-10-16 21:52:44 +02:00
commit 839e5e57c5
126 files changed
+22447

No files matched your search

+451
View File
@@ -0,0 +1,451 @@
<?php
declare(strict_types=1);
/**
* Export Word (.docx) du CV au format Europass.
* Même principe que ExportWord : le fichier OOXML est écrit à la main avec ZipArchive.
* Reçoit le contenu préparé par donnees_europass(), comme l'aperçu HTML.
*
* Police : Arial dans Word (Open Sans n'est pas installée partout) ; l'aperçu et le PDF utilisent Open Sans.
* Unités : twips (1 mm = 56,7), demi-points pour les corps, huitièmes de point pour les filets.
*/
final class ExportEuropass
{
private const BLEU = '004B80';
private const BLEU_CLAIR = '4D9ACB';
private const FILET = 'C0DBED';
private const FOND_LIGNE = 'F2F8FC';
private const TEXTE = '1A1A1A';
private const GRIS = '404040';
private const GRIS_CLAIR = '6F6F6F';
private const PAGE_L = 11906;
private const PAGE_H = 16838;
private const MARGE = 794; // 14 mm
private const TEXTE_L = self::PAGE_L - 2 * self::MARGE;
private const NS = 'xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" '
. 'xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" '
. 'xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" '
. 'xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" '
. 'xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"';
/**
* @param array $d contenu renvoyé par donnees_europass()
* @param string $logo chemin du logo Europass blanc (PNG transparent)
* @param ?string $photo chemin de la photo JPEG, ou null
*/
public static function generer(array $d, string $logo, ?string $photo = null): string
{
$tmp = tempnam(sys_get_temp_dir(), 'europass');
$zip = new ZipArchive();
if ($tmp === false || $zip->open($tmp, ZipArchive::OVERWRITE) !== true) {
throw new RuntimeException(t('Le fichier Word n\'a pas pu être créé.'));
}
$avecPhoto = $photo !== null && is_file($photo);
$zip->addFromString('[Content_Types].xml', self::types());
$zip->addFromString('_rels/.rels', self::relsRacine());
$zip->addFromString('docProps/core.xml', self::proprietes($d));
$zip->addFromString('word/document.xml', self::document($d));
$zip->addFromString('word/header1.xml', self::entete($d, $logo, $avecPhoto ? $photo : null));
$zip->addFromString('word/header2.xml', '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:hdr ' . self::NS . '>' . self::p('') . '</w:hdr>');
$zip->addFromString('word/_rels/header1.xml.rels', self::relsEntete($avecPhoto));
$zip->addFromString('word/styles.xml', self::styles(['fr' => 'fr-FR', 'en' => 'en-GB', 'de' => 'de-DE'][$d['langue']] ?? 'fr-FR'));
$zip->addFromString('word/settings.xml', self::reglages());
$zip->addFromString('word/footer1.xml', self::pied());
$zip->addFromString('word/_rels/document.xml.rels', self::relsDocument());
$zip->addFromString('word/media/logo.png', (string) file_get_contents($logo));
if ($avecPhoto) {
$zip->addFromString('word/media/photo.jpg', (string) file_get_contents($photo));
}
$zip->close();
$contenu = (string) file_get_contents($tmp);
unlink($tmp);
return $contenu;
}
// -----------------------------------------------------------------------
// Briques
// -----------------------------------------------------------------------
private static function x(string $texte): string
{
$texte = preg_replace('/[^\x{9}\x{A}\x{D}\x{20}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', '', $texte) ?? '';
return htmlspecialchars($texte, ENT_XML1 | ENT_QUOTES, 'UTF-8');
}
/** Options : b, i, caps, color, sz (demi-points). */
private static function r(string $texte, array $o = []): string
{
$p = '';
if (!empty($o['b'])) { $p .= '<w:b/>'; }
if (!empty($o['i'])) { $p .= '<w:i/>'; }
if (!empty($o['caps'])) { $p .= '<w:caps/>'; }
if (isset($o['color'])) { $p .= '<w:color w:val="' . $o['color'] . '"/>'; }
if (isset($o['sz'])) { $p .= '<w:sz w:val="' . $o['sz'] . '"/><w:szCs w:val="' . $o['sz'] . '"/>'; }
return '<w:r>' . ($p !== '' ? '<w:rPr>' . $p . '</w:rPr>' : '') . '<w:t xml:space="preserve">' . self::x($texte) . '</w:t></w:r>';
}
/** Options : style, avant, apres, keepNext, retrait (gauche), suspendu, jc, bordureBas [taille, couleur]. */
private static function p(string $runs, array $o = []): string
{
$p = '';
if (isset($o['style'])) { $p .= '<w:pStyle w:val="' . $o['style'] . '"/>'; }
if (!empty($o['keepNext'])) { $p .= '<w:keepNext/>'; }
if (isset($o['bordureBas'])) {
$p .= '<w:pBdr><w:bottom w:val="single" w:sz="' . $o['bordureBas'][0] . '" w:space="4" w:color="' . $o['bordureBas'][1] . '"/></w:pBdr>';
}
if (isset($o['avant']) || isset($o['apres'])) {
$p .= '<w:spacing' . (isset($o['avant']) ? ' w:before="' . $o['avant'] . '"' : '') . (isset($o['apres']) ? ' w:after="' . $o['apres'] . '"' : '') . '/>';
}
if (isset($o['retrait'])) {
$p .= '<w:ind w:left="' . $o['retrait'] . '"' . (isset($o['suspendu']) ? ' w:hanging="' . $o['suspendu'] . '"' : '') . '/>';
}
if (isset($o['jc'])) { $p .= '<w:jc w:val="' . $o['jc'] . '"/>'; }
return '<w:p>' . ($p !== '' ? '<w:pPr>' . $p . '</w:pPr>' : '') . $runs . '</w:p>';
}
private static function tc(int $largeur, string $contenu, array $o = []): string
{
$p = '<w:tcW w:w="' . $largeur . '" w:type="dxa"/>';
if (isset($o['span'])) { $p .= '<w:gridSpan w:val="' . $o['span'] . '"/>'; }
if (isset($o['bordures'])) { $p .= '<w:tcBorders>' . $o['bordures'] . '</w:tcBorders>'; }
if (isset($o['fond'])) { $p .= '<w:shd w:val="clear" w:color="auto" w:fill="' . $o['fond'] . '"/>'; }
if (isset($o['marges'])) {
[$h, $dr, $b, $g] = $o['marges'];
$p .= '<w:tcMar><w:top w:w="' . $h . '" w:type="dxa"/><w:left w:w="' . $g . '" w:type="dxa"/>'
. '<w:bottom w:w="' . $b . '" w:type="dxa"/><w:right w:w="' . $dr . '" w:type="dxa"/></w:tcMar>';
}
$p .= '<w:vAlign w:val="' . ($o['vAlign'] ?? 'top') . '"/>';
return '<w:tc><w:tcPr>' . $p . '</w:tcPr>' . $contenu . '</w:tc>';
}
private static function tbl(array $colonnes, string $lignes, int $retrait = 0): string
{
$grille = '';
foreach ($colonnes as $l) {
$grille .= '<w:gridCol w:w="' . $l . '"/>';
}
return '<w:tbl><w:tblPr><w:tblW w:w="' . array_sum($colonnes) . '" w:type="dxa"/>'
. '<w:tblInd w:w="' . $retrait . '" w:type="dxa"/><w:tblLayout w:type="fixed"/>'
. '<w:tblCellMar><w:left w:w="0" w:type="dxa"/><w:right w:w="0" w:type="dxa"/></w:tblCellMar></w:tblPr>'
. '<w:tblGrid>' . $grille . '</w:tblGrid>' . $lignes . '</w:tbl>';
}
private static function filet(string $cote, int $taille, string $couleur): string
{
return '<w:' . $cote . ' w:val="single" w:sz="' . $taille . '" w:space="0" w:color="' . $couleur . '"/>';
}
/** Image en ligne. $l et $h en mm. */
private static function image(string $rid, int $id, string $nom, float $l, float $h): string
{
$cx = (int) round($l * 36000);
$cy = (int) round($h * 36000);
return '<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0">'
. '<wp:extent cx="' . $cx . '" cy="' . $cy . '"/><wp:docPr id="' . $id . '" name="' . $nom . '" descr="' . $nom . '"/>'
. '<a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic>'
. '<pic:nvPicPr><pic:cNvPr id="' . $id . '" name="' . $nom . '"/><pic:cNvPicPr/></pic:nvPicPr>'
. '<pic:blipFill><a:blip r:embed="' . $rid . '"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill>'
. '<pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="' . $cx . '" cy="' . $cy . '"/></a:xfrm>'
. '<a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr>'
. '</pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>';
}
private static function titre(string $texte): string
{
return self::p(self::r('● ', ['color' => self::BLEU_CLAIR, 'sz' => 16]) . self::r($texte), ['style' => 'TitreEuropass']);
}
// -----------------------------------------------------------------------
// Document
// -----------------------------------------------------------------------
private static function document(array $d): string
{
$L = $d['libelles'];
$corps = '';
if ($d['resume']) {
$corps .= self::titre($L['apropos']);
foreach ($d['resume'] as $l) {
$corps .= self::p(self::r($l));
}
}
if ($d['formations']) {
$corps .= self::titre($L['formation']);
foreach ($d['formations'] as $i => $f) {
$dernier = $i === count($d['formations']) - 1;
if (trim($f['dates'] . $f['lieu']) !== '') {
$corps .= self::p(self::r(trim($f['dates'] . ' ' . $f['lieu']), ['color' => self::GRIS_CLAIR, 'sz' => 17]), ['apres' => 20, 'keepNext' => true]);
}
$corps .= self::p(
self::r($f['diplome'], ['b' => true, 'color' => self::GRIS, 'sz' => 20])
. ($f['etablissement'] !== '' ? self::r(' ' . $f['etablissement'], ['color' => self::GRIS, 'sz' => 20]) : ''),
['apres' => 120] + ($dernier ? [] : ['bordureBas' => [4, self::FILET]])
);
}
}
if ($d['experiences']) {
$corps .= self::titre($L['experience']);
foreach ($d['experiences'] as $i => $x) {
$dernier = $i === count($d['experiences']) - 1;
if ($x['employeur'] !== '' || $x['lieu'] !== '') {
$corps .= self::p(
self::r($x['employeur'], ['b' => true]) . ($x['employeur'] !== '' && $x['lieu'] !== '' ? self::r(' – ') : '') . self::r($x['lieu']),
['apres' => 20, 'keepNext' => true]
);
}
$corps .= self::p(
self::r($x['poste'], ['b' => true, 'color' => self::GRIS, 'sz' => 20]) . self::r(' – ' . $x['dates'], ['color' => self::BLEU, 'sz' => 20]),
['apres' => 40, 'keepNext' => true]
);
foreach ($x['details'] as [$libelle, $valeur]) {
$corps .= self::p(self::r($libelle . $d['deux_points'] . ' ', ['b' => true]) . self::r($valeur), ['apres' => 20]);
}
if ($x['realisations']) {
$corps .= self::p(self::r($L['realisations'], ['b' => true]), ['avant' => 40, 'apres' => 20, 'keepNext' => true]);
foreach ($x['realisations'] as $r) {
$corps .= self::p(self::r('•') . '<w:r><w:tab/></w:r>' . self::r($r), ['retrait' => 600, 'suspendu' => 240, 'apres' => 10]);
}
}
$corps .= self::p('', ['apres' => 100] + ($dernier ? [] : ['bordureBas' => [4, self::FILET]]));
}
}
if ($d['maternelles'] || $d['autres']) {
$corps .= self::titre($L['langues']);
if ($d['maternelles']) {
$corps .= self::p(self::r($L['maternelles'] . ' ') . self::r(implode(' | ', $d['maternelles']), ['b' => true]));
}
if ($d['autres']) {
$corps .= self::p(self::r($L['autres']), ['avant' => 60]);
$corps .= $d['avec_grille'] ? self::grille($d) : self::p(implode(self::r(' '), array_map(
static fn ($l) => self::r($l['langue'], ['b' => true]) . ($l['niveau'] !== '' ? self::r($d['deux_points'] . ' ' . $l['niveau']) : ''),
$d['autres']
)));
$corps .= self::p(self::r($L['niveaux'], ['i' => true, 'color' => self::GRIS_CLAIR, 'sz' => 16]), ['avant' => 40]);
}
}
if ($d['competences']) {
$corps .= self::titre($L['competences']);
foreach ($d['competences'] as $groupe => $liste) {
$corps .= self::p(self::r($groupe, ['b' => true, 'color' => self::GRIS_CLAIR]), ['avant' => 80, 'apres' => 20, 'keepNext' => true]);
$corps .= self::p(self::r(implode(' | ', $liste)));
}
}
if ($d['benevolat']) {
$corps .= self::titre($L['benevolat']);
foreach ($d['benevolat'] as $b) {
$corps .= self::p(self::r($b, ['b' => true]));
}
}
if ($d['permis']) {
$corps .= self::titre($L['permis']);
foreach ($d['permis'] as $p) {
$corps .= self::p(self::r($L['permis_ligne'] . ' ', ['b' => true]) . self::r($p));
}
}
// Première page : le bandeau est dans son en-tête ; les pages suivantes ont un en-tête vide et une marge haute
$section = '<w:sectPr>'
. '<w:headerReference w:type="first" r:id="rId4"/><w:headerReference w:type="default" r:id="rId5"/>'
. '<w:footerReference w:type="default" r:id="rId3"/><w:footerReference w:type="first" r:id="rId3"/>'
. '<w:pgSz w:w="' . self::PAGE_L . '" w:h="' . self::PAGE_H . '"/>'
. '<w:pgMar w:top="850" w:right="' . self::MARGE . '" w:bottom="850" w:left="' . self::MARGE . '" w:header="0" w:footer="340" w:gutter="0"/>'
. '<w:titlePg/>'
. '</w:sectPr>';
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<w:document ' . self::NS . '><w:body>' . $corps . $section . '</w:body></w:document>';
}
/** En-tête de la première page : bandeau bleu de bord à bord, avec photo, nom, titre, coordonnées et logo. */
private static function entete(array $d, string $logo, ?string $photo): string
{
$bleu = ['fond' => self::BLEU];
[$lpx, $hpx] = getimagesize($logo) ?: [700, 142];
$logoL = 34.0;
$logoH = round($logoL * $hpx / $lpx, 1);
$lLogo = 2400;
$lPhoto = $photo ? 2000 : 0;
$lTexte = self::PAGE_L - 2 * self::MARGE - $lLogo - $lPhoto;
$identite = self::p(self::r($d['nom'], ['b' => true, 'color' => 'FFFFFF', 'sz' => 34]), ['apres' => 20]);
if ($d['titre'] !== '') {
$identite .= self::p(self::r($d['titre'], ['color' => 'DBE9F4', 'sz' => 21]), ['apres' => 60]);
}
if ($d['coordonnees']) {
$runs = '';
foreach ($d['coordonnees'] as $i => $k) {
$runs .= ($i ? self::r(' | ', ['color' => '6E95B8', 'sz' => 17]) : '')
. self::r($k['libelle'] . $d['deux_points'] . ' ', ['b' => true, 'color' => 'FFFFFF', 'sz' => 17])
. self::r($k['valeur'], ['color' => 'FFFFFF', 'sz' => 17]);
}
$identite .= self::p($runs, ['apres' => 0]);
}
$vide = self::p('', ['apres' => 0]);
$cellules = self::tc(self::MARGE, $vide, $bleu);
if ($photo) {
$cellules .= self::tc($lPhoto, self::p(self::image('rId2', 2, 'Photo', 28, 28), ['apres' => 0]), $bleu + ['vAlign' => 'center']);
}
$cellules .= self::tc($lTexte, $identite, $bleu + ['vAlign' => 'center', 'marges' => [0, 200, 0, 0]])
. self::tc($lLogo, self::p(self::image('rId1', 1, 'europass', $logoL, $logoH), ['apres' => 0, 'jc' => 'right']), $bleu + ['vAlign' => 'top'])
. self::tc(self::MARGE, $vide, $bleu);
$colonnes = array_values(array_filter([self::MARGE, $lPhoto, $lTexte, $lLogo, self::MARGE]));
$lignes = '<w:tr><w:trPr><w:trHeight w:val="340" w:hRule="exact"/></w:trPr>' . self::tc(self::PAGE_L, $vide, $bleu + ['span' => count($colonnes)]) . '</w:tr>'
. '<w:tr>' . $cellules . '</w:tr>'
. '<w:tr><w:trPr><w:trHeight w:val="300" w:hRule="exact"/></w:trPr>' . self::tc(self::PAGE_L, $vide, $bleu + ['span' => count($colonnes)]) . '</w:tr>';
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:hdr ' . self::NS . '>'
. self::tbl($colonnes, $lignes, -self::MARGE) . self::p('', ['apres' => 0]) . '</w:hdr>';
}
/** Grille européenne des langues. */
private static function grille(array $d): string
{
$L = $d['libelles'];
$lLangue = 1900;
$lCase = intdiv(self::TEXTE_L - $lLangue, 5);
$colonnes = [$lLangue, $lCase, $lCase, $lCase, $lCase, $lCase];
$centre = static fn (string $runs): string => self::p($runs, ['jc' => 'center', 'apres' => 0]);
$haut = self::filet('top', 6, self::BLEU_CLAIR);
$bas = self::filet('bottom', 6, self::BLEU_CLAIR);
$marges = ['marges' => [60, 40, 60, 40]];
$lignes = '<w:tr>' . self::tc($lLangue, self::p(''), $marges)
. self::tc(2 * $lCase, $centre(self::r(mb_strtoupper($L['comprehension']), ['b' => true, 'sz' => 17])), $marges + ['span' => 2, 'bordures' => $bas])
. self::tc(2 * $lCase, $centre(self::r(mb_strtoupper($L['expression']), ['b' => true, 'sz' => 17])), $marges + ['span' => 2, 'bordures' => $bas])
. self::tc($lCase, $centre(self::r(mb_strtoupper($L['ecrit']), ['b' => true, 'sz' => 17])), $marges + ['bordures' => $bas])
. '</w:tr>';
$sous = self::tc($lLangue, self::p(''), $marges);
foreach (array_slice($L['grille'], 0, 4) as $g) {
$sous .= self::tc($lCase, $centre(self::r($g, ['sz' => 16])), $marges);
}
$lignes .= '<w:tr>' . $sous . self::tc($lCase, self::p(''), $marges) . '</w:tr>';
foreach ($d['autres'] as $i => $l) {
$fond = $i % 2 === 0 ? ['fond' => self::FOND_LIGNE] : [];
$bord = ['bordures' => $haut . ($i === count($d['autres']) - 1 ? $bas : '')];
$cellules = self::tc($lLangue, self::p(self::r($l['langue'], ['b' => true, 'sz' => 17]), ['apres' => 0]), $marges + $fond + $bord + ['vAlign' => 'center']);
if ($l['grille']) {
foreach ($l['grille'] as $niveau) {
$cellules .= self::tc($lCase, $centre(self::r($niveau ?: '–', ['sz' => 17])), $marges + $fond + $bord + ['vAlign' => 'center']);
}
} else {
$cellules .= self::tc(5 * $lCase, $centre(self::r($l['niveau'] !== '' ? $L['niveau'] . ' ' . $l['niveau'] : '–', ['sz' => 17])), $marges + $fond + $bord + ['span' => 5, 'vAlign' => 'center']);
}
$lignes .= '<w:tr><w:trPr><w:cantSplit/></w:trPr>' . $cellules . '</w:tr>';
}
return self::tbl($colonnes, $lignes);
}
private static function pied(): string
{
$petit = ['color' => self::GRIS_CLAIR, 'sz' => 14];
$champ = static fn (string $nom): string => '<w:fldSimple w:instr=" ' . $nom . ' ">' . self::r('1', $petit) . '</w:fldSimple>';
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<w:ftr ' . self::NS . '>'
. self::p($champ('PAGE') . self::r(' / ', $petit) . $champ('NUMPAGES'), ['jc' => 'right', 'apres' => 0])
. '</w:ftr>';
}
// -----------------------------------------------------------------------
// Fichiers fixes
// -----------------------------------------------------------------------
private static function styles(string $langue): string
{
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">'
. '<w:docDefaults>'
. '<w:rPrDefault><w:rPr><w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial" w:eastAsia="Arial"/>'
. '<w:color w:val="' . self::TEXTE . '"/><w:sz w:val="19"/><w:szCs w:val="19"/><w:lang w:val="' . $langue . '"/></w:rPr></w:rPrDefault>'
. '<w:pPrDefault><w:pPr><w:spacing w:before="0" w:after="40" w:line="264" w:lineRule="auto"/></w:pPr></w:pPrDefault>'
. '</w:docDefaults>'
. '<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style>'
. '<w:style w:type="paragraph" w:styleId="TitreEuropass"><w:name w:val="heading 2"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/>'
. '<w:pPr><w:keepNext/><w:pBdr><w:bottom w:val="single" w:sz="12" w:space="2" w:color="' . self::BLEU_CLAIR . '"/></w:pBdr>'
. '<w:spacing w:before="200" w:after="120"/><w:ind w:left="-280"/><w:outlineLvl w:val="1"/></w:pPr>'
. '<w:rPr><w:b/><w:caps/><w:sz w:val="22"/><w:szCs w:val="22"/></w:rPr></w:style>'
. '</w:styles>';
}
private static function reglages(): string
{
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">'
. '<w:defaultTabStop w:val="708"/>'
. '<w:compat><w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="15"/></w:compat>'
. '</w:settings>';
}
private static function types(): string
{
$wml = 'application/vnd.openxmlformats-officedocument.wordprocessingml.';
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
. '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
. '<Default Extension="xml" ContentType="application/xml"/>'
. '<Default Extension="png" ContentType="image/png"/>'
. '<Default Extension="jpg" ContentType="image/jpeg"/>'
. '<Override PartName="/word/document.xml" ContentType="' . $wml . 'document.main+xml"/>'
. '<Override PartName="/word/styles.xml" ContentType="' . $wml . 'styles+xml"/>'
. '<Override PartName="/word/settings.xml" ContentType="' . $wml . 'settings+xml"/>'
. '<Override PartName="/word/footer1.xml" ContentType="' . $wml . 'footer+xml"/>'
. '<Override PartName="/word/header1.xml" ContentType="' . $wml . 'header+xml"/>'
. '<Override PartName="/word/header2.xml" ContentType="' . $wml . 'header+xml"/>'
. '<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>'
. '</Types>';
}
private static function relsRacine(): string
{
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
. '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>'
. '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>'
. '</Relationships>';
}
private static function relsDocument(): string
{
$t = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/';
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
. '<Relationship Id="rId1" Type="' . $t . 'styles" Target="styles.xml"/>'
. '<Relationship Id="rId2" Type="' . $t . 'settings" Target="settings.xml"/>'
. '<Relationship Id="rId3" Type="' . $t . 'footer" Target="footer1.xml"/>'
. '<Relationship Id="rId4" Type="' . $t . 'header" Target="header1.xml"/>'
. '<Relationship Id="rId5" Type="' . $t . 'header" Target="header2.xml"/>'
. '</Relationships>';
}
private static function relsEntete(bool $photo): string
{
$t = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/';
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
. '<Relationship Id="rId1" Type="' . $t . 'image" Target="media/logo.png"/>'
. ($photo ? '<Relationship Id="rId2" Type="' . $t . 'image" Target="media/photo.jpg"/>' : '')
. '</Relationships>';
}
private static function proprietes(array $d): string
{
$date = gmdate('Y-m-d\TH:i:s\Z');
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" '
. 'xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" '
. 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
. '<dc:title>' . self::x($d['fichier']) . '</dc:title><dc:creator>IT Club</dc:creator>'
. '<dcterms:created xsi:type="dcterms:W3CDTF">' . $date . '</dcterms:created>'
. '<dcterms:modified xsi:type="dcterms:W3CDTF">' . $date . '</dcterms:modified>'
. '</cp:coreProperties>';
}
}