Publier Globinours 1.0.0-rc.3
This commit is contained in:
parent
ea8c24d622
commit
9a2b4068da
325 changed files with 38230 additions and 20 deletions
537
app/Services/DocumentsService.php
Normal file
537
app/Services/DocumentsService.php
Normal file
|
|
@ -0,0 +1,537 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
final class DocumentsService
|
||||
{
|
||||
private const ADOPTION_TEMPLATE = __DIR__ . '/../../resources/documents/CONTRAT ADOPTION V1.5.odt';
|
||||
private const TEMPLATES = [
|
||||
'adoption' => self::ADOPTION_TEMPLATE,
|
||||
'abandon' => __DIR__ . '/../../resources/documents/ABANDON V1.0.odt',
|
||||
'benevole' => __DIR__ . '/../../resources/documents/CONTRAT BENEVOLE V2.0.odt',
|
||||
'fa' => __DIR__ . '/../../resources/documents/PROPOSITION FA V1.1.odt',
|
||||
'pre_adoption' => __DIR__ . '/../../resources/documents/CERTIFICAT VISITE PRE-ADOPTION V1.1.odt',
|
||||
];
|
||||
|
||||
public static function adoptionTemplate(): string
|
||||
{
|
||||
if (!is_file(self::ADOPTION_TEMPLATE)) {
|
||||
throw new RuntimeException(t('service.document.adoption_missing'));
|
||||
}
|
||||
return self::ADOPTION_TEMPLATE;
|
||||
}
|
||||
|
||||
public static function template(string $type): string
|
||||
{
|
||||
$path = self::TEMPLATES[$type] ?? null;
|
||||
if (!$path || !is_file($path)) {
|
||||
throw new RuntimeException(t('service.document.template_missing'));
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
public static function generateAbandon(array $animal): string
|
||||
{
|
||||
return self::transformTemplate('abandon', 'abandon-' . self::safeName((string) $animal['name']), function (
|
||||
DOMXPath $xp,
|
||||
) use ($animal): void {
|
||||
$sex = match ($animal['sex'] ?? 'U') {
|
||||
'M' => 'Mâle',
|
||||
'F' => 'Femelle',
|
||||
default => 'Non renseigné',
|
||||
};
|
||||
$birth = trim((string) ($animal['birth_date'] ?? ''));
|
||||
$age = '—';
|
||||
if ($birth !== '') {
|
||||
$born = new DateTimeImmutable($birth);
|
||||
$now = new DateTimeImmutable();
|
||||
$diff = $born->diff($now);
|
||||
$age = $diff->y > 0 ? $diff->y . ' an' . ($diff->y > 1 ? 's' : '') : $diff->m . ' mois';
|
||||
}
|
||||
$chip = trim((string) ($animal['chip'] ?: $animal['chip_id'] ?? ''));
|
||||
self::replaceParagraph($xp, 'Nom de l’animal', [
|
||||
['Nom de l’animal : ', 'label'],
|
||||
[(string) $animal['name'], 'value'],
|
||||
['tab'],
|
||||
['Sexe : ', 'label'],
|
||||
[$sex, 'value'],
|
||||
]);
|
||||
self::replaceParagraph($xp, 'Race :', [
|
||||
['Race : ', 'label'],
|
||||
[(string) ($animal['breed'] ?? '—'), 'value'],
|
||||
['tab'],
|
||||
['Âge : ', 'label'],
|
||||
[$age, 'value'],
|
||||
['break'],
|
||||
['Vaccination : ', 'label'],
|
||||
['À vérifier', 'value'],
|
||||
['tab'],
|
||||
['Vermifugation : ', 'label'],
|
||||
['À vérifier', 'value'],
|
||||
['break'],
|
||||
['Numéro de tatouage ou puce électronique : ', 'label'],
|
||||
[$chip !== '' ? $chip : '—', 'value'],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public static function generateContact(string $type, array $contact): string
|
||||
{
|
||||
if (!in_array($type, ['benevole', 'fa'], true)) {
|
||||
throw new InvalidArgumentException(t('service.document.invalid_type'));
|
||||
}
|
||||
return self::transformTemplate($type, $type . '-' . self::safeName((string) $contact['name']), function (
|
||||
DOMXPath $xp,
|
||||
) use ($type, $contact): void {
|
||||
$name = (string) $contact['name'];
|
||||
$address = (string) ($contact['address'] ?? '');
|
||||
$postal = (string) ($contact['postal_code'] ?? '');
|
||||
$city = (string) ($contact['city'] ?? '');
|
||||
$phone = (string) ($contact['phone'] ?? '');
|
||||
$email = (string) ($contact['email'] ?? '');
|
||||
if ($type === 'benevole') {
|
||||
self::replaceParagraph(
|
||||
$xp,
|
||||
'Nom :',
|
||||
[['Nom : ', 'label'], [$name, 'value'], ['tab'], ['Prénom : ', 'label'], ['', 'value']],
|
||||
0,
|
||||
);
|
||||
self::replaceParagraph($xp, 'Adresse :', [['Adresse : ', 'label'], [$address, 'value']], 0);
|
||||
self::replaceParagraph(
|
||||
$xp,
|
||||
'Code Postal :',
|
||||
[['Code Postal : ', 'label'], [$postal, 'value'], ['tab'], ['Ville : ', 'label'], [$city, 'value']],
|
||||
0,
|
||||
);
|
||||
self::replaceParagraph(
|
||||
$xp,
|
||||
'Numéro de téléphone',
|
||||
[
|
||||
['Numéro de téléphone : ', 'label'],
|
||||
[$phone, 'value'],
|
||||
['tab'],
|
||||
['Adresse e-mail : ', 'label'],
|
||||
[$email, 'value'],
|
||||
],
|
||||
0,
|
||||
);
|
||||
} else {
|
||||
self::replaceParagraph($xp, 'NOM / Prénom :', [['NOM / Prénom : ', 'label'], [$name, 'value']], 0);
|
||||
self::replaceParagraph($xp, 'Adresse :', [['Adresse : ', 'label'], [$address, 'value']], 0);
|
||||
self::replaceParagraph(
|
||||
$xp,
|
||||
'Code postal :',
|
||||
[['Code postal : ', 'label'], [$postal, 'value'], ['tab'], ['Ville : ', 'label'], [$city, 'value']],
|
||||
0,
|
||||
);
|
||||
self::replaceParagraph(
|
||||
$xp,
|
||||
'Téléphone :',
|
||||
[
|
||||
['Téléphone : ', 'label'],
|
||||
[$phone, 'value'],
|
||||
['tab'],
|
||||
['Adresse mail : ', 'label'],
|
||||
[$email, 'value'],
|
||||
],
|
||||
0,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static function generateAdoption(array $animal, ?array $adoption, ?array $vaccine): string
|
||||
{
|
||||
$dir = self::tempDir();
|
||||
$name = self::safeName((string) $animal['name']);
|
||||
$target = $dir . '/contrat-adoption-' . $name . '.odt';
|
||||
if (!copy(self::adoptionTemplate(), $target)) {
|
||||
throw new RuntimeException(t('service.document.copy_failed'));
|
||||
}
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($target) !== true) {
|
||||
throw new RuntimeException(t('service.document.open_failed'));
|
||||
}
|
||||
$xml = $zip->getFromName('content.xml');
|
||||
if ($xml === false) {
|
||||
$zip->close();
|
||||
throw new RuntimeException(t('service.document.content_invalid'));
|
||||
}
|
||||
$dom = new DOMDocument();
|
||||
$dom->preserveWhiteSpace = true;
|
||||
if (!$dom->loadXML($xml)) {
|
||||
$zip->close();
|
||||
throw new RuntimeException(t('service.document.xml_invalid'));
|
||||
}
|
||||
$xp = new DOMXPath($dom);
|
||||
$xp->registerNamespace('text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0');
|
||||
$xp->registerNamespace('office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
|
||||
self::addGeneratedFieldStyles($dom, $xp);
|
||||
|
||||
$date = static fn(?string $value): string => $value ? date('d/m/Y', strtotime($value)) : '';
|
||||
$sex = match ($animal['sex'] ?? 'U') {
|
||||
'M' => '☒ Mâle ☐ Femelle',
|
||||
'F' => '☐ Mâle ☒ Femelle',
|
||||
default => '☐ Mâle ☐ Femelle',
|
||||
};
|
||||
$origin = trim((string) ($animal['breed'] ?? ''));
|
||||
$chip = trim((string) ($animal['chip'] ?: $animal['chip_id'] ?? ''));
|
||||
$testStatus =
|
||||
'FIV ' .
|
||||
self::testLabel((string) ($animal['fiv_status'] ?? 'unknown')) .
|
||||
' / FeLV ' .
|
||||
self::testLabel((string) ($animal['felv_status'] ?? 'unknown'));
|
||||
$signatureDate = $date($adoption['adoption_date'] ?? date('Y-m-d'));
|
||||
|
||||
self::setSpan($xp, 'T26', (string) $animal['name']);
|
||||
self::setLabeledSpan($xp, 'T28', 'Sexe : ', $sex);
|
||||
self::setSpan($xp, 'T31', ' ' . $date($animal['birth_date'] ?? null));
|
||||
self::setSpan($xp, 'T33', 'Robe : ', null, 'GeneratedField');
|
||||
self::setSpan($xp, 'T34', (string) ($animal['color'] ?? ''));
|
||||
self::setSpan($xp, 'T41', $origin !== '' ? $origin : '—');
|
||||
self::setSpan($xp, 'T42', '');
|
||||
self::setLabeledSpan($xp, 'T45', 'Puce électronique N° : ', $chip !== '' ? $chip : '—');
|
||||
self::setLabeledSpan(
|
||||
$xp,
|
||||
'T49',
|
||||
' : ',
|
||||
(int) ($animal['sterilized'] ?? 0) === 1
|
||||
? '☒ Effectuée ☐ À effectuer avant le :'
|
||||
: '☐ Effectuée ☒ À effectuer avant le :',
|
||||
);
|
||||
self::setLabeledSpan($xp, 'T53', 'Date du test FIV-FELV : ', $testStatus, 'tab');
|
||||
self::setLabeledSpan($xp, 'T55', 'Date de la dernière vaccination : ', $date($vaccine['done_date'] ?? null));
|
||||
self::setLabeledSpan($xp, 'T56', 'Rappel à effectuer le : ', $date($vaccine['due_date'] ?? null), 'tab');
|
||||
|
||||
self::setSpan($xp, 'T98', (string) ($adoption['adopter_name'] ?? ''));
|
||||
self::setSpan($xp, 'T101', (string) ($adoption['adopter_address'] ?? ''));
|
||||
self::setSpan($xp, 'T104', (string) ($adoption['adopter_postal_code'] ?? ''));
|
||||
self::setSpan($xp, 'T107', (string) ($adoption['adopter_city'] ?? ''));
|
||||
self::setSpan($xp, 'T110', '');
|
||||
self::setSpan($xp, 'T113', (string) ($adoption['adopter_phone'] ?? ''));
|
||||
self::setSpan($xp, 'T116', (string) ($adoption['adopter_email'] ?? ''));
|
||||
$signatureCity = AppSettings::get('association_city') ?: '—';
|
||||
self::setLabeledSpan($xp, 'T155', 'Fait à : ', $signatureCity, null, 'GeneratedSignature');
|
||||
self::setLabeledSpan($xp, 'T156', 'Le : ', $signatureDate, 'tab', 'GeneratedSignature');
|
||||
self::setLabeledSpan($xp, 'T247', 'Fait à : ', $signatureCity, null, 'GeneratedSignature');
|
||||
self::setLabeledSpan($xp, 'T253', 'Le : ', $signatureDate, 'tab', 'GeneratedSignature');
|
||||
self::setSpan($xp, 'T283', (string) $animal['name']);
|
||||
self::setLabeledSpan($xp, 'T367', 'Fait à : ', $signatureCity, null, 'GeneratedSignature');
|
||||
self::setLabeledSpan($xp, 'T373', 'Le : ', $signatureDate, 'tab', 'GeneratedSignature');
|
||||
foreach (
|
||||
[
|
||||
'T25',
|
||||
'T30',
|
||||
'T33',
|
||||
'T36',
|
||||
'T40',
|
||||
'T45',
|
||||
'T48',
|
||||
'T52',
|
||||
'T53',
|
||||
'T55',
|
||||
'T56',
|
||||
'T97',
|
||||
'T100',
|
||||
'T103',
|
||||
'T106',
|
||||
'T109',
|
||||
'T112',
|
||||
'T115',
|
||||
'T281',
|
||||
]
|
||||
as $labelStyle
|
||||
) {
|
||||
self::applySpanStyle($xp, $labelStyle);
|
||||
}
|
||||
|
||||
$updated = $dom->saveXML();
|
||||
if ($updated === false || !$zip->addFromString('content.xml', $updated)) {
|
||||
$zip->close();
|
||||
throw new RuntimeException(t('service.document.update_contract_failed'));
|
||||
}
|
||||
$zip->close();
|
||||
return $target;
|
||||
}
|
||||
|
||||
public static function toPdf(string $odtPath): string
|
||||
{
|
||||
$dir = dirname($odtPath);
|
||||
$profile = $dir . '/lo-profile';
|
||||
mkdir($profile, 0775, true);
|
||||
$cmd = [
|
||||
'/usr/bin/env',
|
||||
'-u',
|
||||
'DISPLAY',
|
||||
'SAL_USE_VCLPLUGIN=svp',
|
||||
'/usr/bin/libreoffice',
|
||||
'-env:UserInstallation=' . self::fileUri($profile),
|
||||
'--headless',
|
||||
'--nologo',
|
||||
'--nodefault',
|
||||
'--nolockcheck',
|
||||
'--nofirststartwizard',
|
||||
'--convert-to',
|
||||
'pdf',
|
||||
'--outdir',
|
||||
$dir,
|
||||
$odtPath,
|
||||
];
|
||||
$pipes = [];
|
||||
$process = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes);
|
||||
if (!is_resource($process)) {
|
||||
throw new RuntimeException(t('service.document.libreoffice_failed'));
|
||||
}
|
||||
$stdout = stream_get_contents($pipes[1]);
|
||||
$stderr = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
$code = proc_close($process);
|
||||
$pdf = $dir . '/' . pathinfo($odtPath, PATHINFO_FILENAME) . '.pdf';
|
||||
if ($code !== 0 || !is_file($pdf)) {
|
||||
throw new RuntimeException(t('service.document.pdf_failed', ['details' => trim($stdout . ' ' . $stderr)]));
|
||||
}
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
public static function cleanup(string $path): void
|
||||
{
|
||||
$dir = dirname($path);
|
||||
if (!str_starts_with($dir, sys_get_temp_dir() . '/globinours-doc-')) {
|
||||
return;
|
||||
}
|
||||
$items = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::CHILD_FIRST,
|
||||
);
|
||||
foreach ($items as $item) {
|
||||
$item->isDir() ? rmdir($item->getPathname()) : unlink($item->getPathname());
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
|
||||
private static function setSpan(
|
||||
DOMXPath $xp,
|
||||
string $style,
|
||||
string $value,
|
||||
?string $prefix = null,
|
||||
string $generatedStyle = 'GeneratedValue',
|
||||
): void {
|
||||
$nodes = $xp->query("//text:span[@text:style-name='$style']");
|
||||
if (!$nodes || $nodes->length === 0) {
|
||||
return;
|
||||
}
|
||||
$node = $nodes->item(0);
|
||||
while ($node->firstChild) {
|
||||
$node->removeChild($node->firstChild);
|
||||
}
|
||||
$node->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'text:style-name', $generatedStyle);
|
||||
if ($prefix === 'tab') {
|
||||
$node->appendChild(
|
||||
$node->ownerDocument->createElementNS('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'text:tab'),
|
||||
);
|
||||
}
|
||||
$node->appendChild($node->ownerDocument->createTextNode($value));
|
||||
}
|
||||
|
||||
private static function setLabeledSpan(
|
||||
DOMXPath $xp,
|
||||
string $style,
|
||||
string $label,
|
||||
string $value,
|
||||
?string $prefix = null,
|
||||
string $labelStyle = 'GeneratedField',
|
||||
): void {
|
||||
$nodes = $xp->query("//text:span[@text:style-name='$style']");
|
||||
if (!$nodes || $nodes->length === 0) {
|
||||
return;
|
||||
}
|
||||
$node = $nodes->item(0);
|
||||
while ($node->firstChild) {
|
||||
$node->removeChild($node->firstChild);
|
||||
}
|
||||
$node->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'text:style-name', $labelStyle);
|
||||
if ($prefix === 'tab') {
|
||||
$node->appendChild(
|
||||
$node->ownerDocument->createElementNS('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'text:tab'),
|
||||
);
|
||||
}
|
||||
$node->appendChild($node->ownerDocument->createTextNode($label));
|
||||
$valueSpan = $node->ownerDocument->createElementNS(
|
||||
'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
|
||||
'text:span',
|
||||
);
|
||||
$valueSpan->setAttributeNS(
|
||||
'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
|
||||
'text:style-name',
|
||||
'GeneratedValue',
|
||||
);
|
||||
$valueSpan->appendChild($node->ownerDocument->createTextNode($value));
|
||||
$node->appendChild($valueSpan);
|
||||
}
|
||||
|
||||
private static function applySpanStyle(DOMXPath $xp, string $style, string $generatedStyle = 'GeneratedField'): void
|
||||
{
|
||||
$nodes = $xp->query("//text:span[@text:style-name='$style']");
|
||||
if (!$nodes || $nodes->length === 0) {
|
||||
return;
|
||||
}
|
||||
$nodes
|
||||
->item(0)
|
||||
->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'text:style-name', $generatedStyle);
|
||||
}
|
||||
|
||||
private static function addGeneratedFieldStyles(DOMDocument $dom, DOMXPath $xp): void
|
||||
{
|
||||
$automatic = $xp->query('//office:automatic-styles')->item(0);
|
||||
if (!$automatic) {
|
||||
return;
|
||||
}
|
||||
self::appendTextStyle($dom, $automatic, 'GeneratedField', '11pt');
|
||||
self::appendTextStyle($dom, $automatic, 'GeneratedValue', '11pt', true);
|
||||
self::appendTextStyle($dom, $automatic, 'GeneratedSignature', '11pt');
|
||||
}
|
||||
|
||||
private static function appendTextStyle(
|
||||
DOMDocument $dom,
|
||||
DOMNode $automatic,
|
||||
string $name,
|
||||
string $size,
|
||||
bool $bold = false,
|
||||
): void {
|
||||
$style = $dom->createElementNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:style');
|
||||
$style->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:name', $name);
|
||||
$style->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:family', 'text');
|
||||
$props = $dom->createElementNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:text-properties');
|
||||
$props->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:font-name', 'Open Sans');
|
||||
$props->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:font-name-asian', 'Open Sans');
|
||||
$props->setAttributeNS(
|
||||
'urn:oasis:names:tc:opendocument:xmlns:style:1.0',
|
||||
'style:font-name-complex',
|
||||
'Open Sans',
|
||||
);
|
||||
$props->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', 'fo:font-size', $size);
|
||||
$weight = $bold ? 'bold' : 'normal';
|
||||
$props->setAttributeNS(
|
||||
'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0',
|
||||
'fo:font-weight',
|
||||
$weight,
|
||||
);
|
||||
$props->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:font-size-asian', $size);
|
||||
$props->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:font-size-complex', $size);
|
||||
$props->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:font-weight-asian', $weight);
|
||||
$props->setAttributeNS('urn:oasis:names:tc:opendocument:xmlns:style:1.0', 'style:font-weight-complex', $weight);
|
||||
$style->appendChild($props);
|
||||
$automatic->appendChild($style);
|
||||
}
|
||||
|
||||
private static function transformTemplate(string $type, string $filename, callable $transform): string
|
||||
{
|
||||
$dir = self::tempDir();
|
||||
$target = $dir . '/' . $filename . '.odt';
|
||||
if (!copy(self::template($type), $target)) {
|
||||
throw new RuntimeException(t('service.document.copy_failed'));
|
||||
}
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($target) !== true) {
|
||||
throw new RuntimeException(t('service.document.open_failed'));
|
||||
}
|
||||
$xml = $zip->getFromName('content.xml');
|
||||
if ($xml === false) {
|
||||
$zip->close();
|
||||
throw new RuntimeException(t('service.document.content_invalid'));
|
||||
}
|
||||
$dom = new DOMDocument();
|
||||
$dom->preserveWhiteSpace = true;
|
||||
if (!$dom->loadXML($xml)) {
|
||||
$zip->close();
|
||||
throw new RuntimeException(t('service.document.xml_invalid'));
|
||||
}
|
||||
$xp = new DOMXPath($dom);
|
||||
$xp->registerNamespace('text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0');
|
||||
$xp->registerNamespace('office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
|
||||
self::addGeneratedFieldStyles($dom, $xp);
|
||||
$transform($xp);
|
||||
$updated = $dom->saveXML();
|
||||
if ($updated === false || !$zip->addFromString('content.xml', $updated)) {
|
||||
$zip->close();
|
||||
throw new RuntimeException(t('service.document.update_failed'));
|
||||
}
|
||||
$zip->close();
|
||||
return $target;
|
||||
}
|
||||
|
||||
private static function replaceParagraph(DOMXPath $xp, string $startsWith, array $parts, int $occurrence = 0): void
|
||||
{
|
||||
$matches = [];
|
||||
foreach ($xp->query('//text:p') as $paragraph) {
|
||||
$plain = preg_replace('/\s+/u', ' ', str_replace("\u{00A0}", ' ', $paragraph->textContent));
|
||||
if (str_starts_with(trim((string) $plain), $startsWith)) {
|
||||
$matches[] = $paragraph;
|
||||
}
|
||||
}
|
||||
$node = $matches[$occurrence] ?? null;
|
||||
if (!$node) {
|
||||
return;
|
||||
}
|
||||
while ($node->firstChild) {
|
||||
$node->removeChild($node->firstChild);
|
||||
}
|
||||
foreach ($parts as $part) {
|
||||
if ($part === ['tab']) {
|
||||
$node->appendChild(
|
||||
$node->ownerDocument->createElementNS('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'text:tab'),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if ($part === ['break']) {
|
||||
$node->appendChild(
|
||||
$node->ownerDocument->createElementNS(
|
||||
'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
|
||||
'text:line-break',
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
[$text, $kind] = $part;
|
||||
$span = $node->ownerDocument->createElementNS(
|
||||
'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
|
||||
'text:span',
|
||||
);
|
||||
$span->setAttributeNS(
|
||||
'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
|
||||
'text:style-name',
|
||||
$kind === 'value' ? 'GeneratedValue' : 'GeneratedField',
|
||||
);
|
||||
$span->appendChild($node->ownerDocument->createTextNode($text));
|
||||
$node->appendChild($span);
|
||||
}
|
||||
}
|
||||
|
||||
private static function testLabel(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
'neg' => 'négatif',
|
||||
'pos' => 'positif',
|
||||
default => 'non renseigné',
|
||||
};
|
||||
}
|
||||
private static function safeName(string $name): string
|
||||
{
|
||||
$name = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $name) ?: 'chat';
|
||||
return strtolower(trim(preg_replace('/[^a-zA-Z0-9]+/', '-', $name), '-')) ?: 'chat';
|
||||
}
|
||||
private static function tempDir(): string
|
||||
{
|
||||
$dir = sys_get_temp_dir() . '/globinours-doc-' . bin2hex(random_bytes(8));
|
||||
if (!mkdir($dir, 0775, true)) {
|
||||
throw new RuntimeException(t('service.document.temp_failed'));
|
||||
}
|
||||
return $dir;
|
||||
}
|
||||
private static function fileUri(string $path): string
|
||||
{
|
||||
return 'file://' . str_replace('%2F', '/', rawurlencode($path));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue