Files
Vivier/src/ExportEuropass.php
T

452 lines
25 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
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>';
}
}